Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2161 lines
51 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
utils.c
Abstract:
This module contains the code that is of a general
support nature to the modem driver.
operations in the modem driver.
Author:
Anthony V. Ercolano 29-Aug-1995
Environment:
Kernel mode
Revision History :
--*/
#include "precomp.h"
NTSTATUS
UniSetupNoPassPart1(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
UniSetupNoPassPart2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
UniSetupNoPassPart3(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
UniSetupSniffPart0(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
UniSetupSniffPart1(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
UniSetupSniffPart2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
UniSniffWaitComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
UniPassThroughStarter(
IN PDEVICE_EXTENSION Extension
);
VOID
UniSendOurWaitDown(
IN PDEVICE_EXTENSION Extension
);
VOID
UniPostProcessShuttledWaits(
IN PDEVICE_EXTENSION Extension,
IN PMASKSTATE MaskStates,
IN ULONG MaskValue
);
VOID
UniPreProcessShuttledWaits(
IN PDEVICE_EXTENSION Extension,
IN PMASKSTATE MaskStates,
IN ULONG MaskValue
);
NTSTATUS
UniSetupPass(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
UniCheckPassThrough(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP for the current request
Return Value:
The function value is the final status of the call
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status;
//
// We need to check each of the ioctls that can come through. If they
// set something that intersects with what the owner is doing we need
// to note this so that when the owner doesn't want that info anymore
// it still goes back to the app.
//
if (deviceExtension->PassThrough != MODEM_NOPASSTHROUGH) {
//
// If it is a mask operation, serialize it.
//
if ((irpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_SERIAL_SET_WAIT_MASK) ||
(irpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_SERIAL_WAIT_ON_MASK)) {
return UniStartOrQueue(
deviceExtension,
&deviceExtension->DeviceLock,
Irp,
&deviceExtension->MaskOps,
&deviceExtension->CurrentMaskOp,
UniMaskStarter
);
} else {
//
// If it is a setcommconfig then we can process it
// right here.
//
if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_SERIAL_SET_COMMCONFIG) {
return UniValidateNewCommConfig(
deviceExtension,
Irp,
FALSE
);
} else {
Irp->CurrentLocation++;
Irp->Tail.Overlay.CurrentStackLocation++;
return IoCallDriver(
deviceExtension->AttachedDeviceObject,
Irp
);
}
}
} else {
Irp->IoStatus.Status = STATUS_PORT_DISCONNECTED;
Irp->IoStatus.Information=0L;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return STATUS_PORT_DISCONNECTED;
}
}
NTSTATUS
UniNoCheckPassThrough(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Just let the request go on down. On it's way back up, strip out anything that
the owner added that the application doesn't already want.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP for the current request
Return Value:
The function value is the final status of the call
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
Irp->CurrentLocation++;
Irp->Tail.Overlay.CurrentStackLocation++;
return IoCallDriver(
deviceExtension->AttachedDeviceObject,
Irp
);
}
NTSTATUS
UniStartOrQueue(
IN PDEVICE_EXTENSION Extension,
IN PKSPIN_LOCK QueueLock,
IN PIRP Irp,
IN PLIST_ENTRY QueueToExamine,
IN PIRP *CurrentOpIrp,
IN PUNI_START_ROUTINE Starter
)
/*++
Routine Description:
This routine is used to either start or queue any requst
that can be queued in the driver.
Arguments:
Extension - Points to the device extension.
QueueLock - The lock protecting the particular queue.
Irp - The irp to either queue or start. In either
case the irp will be marked pending.
QueueToExamine - The queue the irp will be place on if there
is already an operation in progress.
CurrentOpIrp - Pointer to a pointer to the irp the is current
for the queue. The pointer pointed to will be
set with to Irp if what CurrentOpIrp points to
is NULL.
Starter - The routine to call if the queue is empty.
Return Value:
This routine will return STATUS_PENDING if the queue is
not empty. Otherwise, it will return the status returned
from the starter routine (or cancel, if the cancel bit is
on in the irp).
--*/
{
KIRQL oldIrql;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
KeAcquireSpinLock(
QueueLock,
&oldIrql
);
//
// Help out the mask operations. If this irp is a mask irp,
// increment the reference count for the appropriate handle.
//
if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_SERIAL_SET_WAIT_MASK) {
Extension->MaskStates[
irpSp->FileObject->FsContext?CONTROL_HANDLE:CLIENT_HANDLE
].SetMaskCount++;
}
if ((IsListEmpty(QueueToExamine)) &&
!(*CurrentOpIrp)) {
//
// There were no current operation. Mark this one as
// current and start it up.
//
*CurrentOpIrp = Irp;
KeReleaseSpinLock(
QueueLock,
oldIrql
);
return Starter(Extension);
} else {
Irp->IoStatus.Status = STATUS_PENDING;
IoMarkIrpPending(Irp);
InsertTailList(
QueueToExamine,
&Irp->Tail.Overlay.ListEntry
);
KeReleaseSpinLock(
QueueLock,
oldIrql
);
return STATUS_PENDING;
}
}
VOID
UniGetNextIrp(
IN PKSPIN_LOCK QueueLock,
IN PIRP *CurrentOpIrp,
IN PLIST_ENTRY QueueToProcess,
OUT PIRP *NextIrp,
IN BOOLEAN CompleteCurrent
)
/*++
Routine Description:
This function is used to make the head of the particular
queue the current irp. It also completes the what
was the old current irp if desired.
Arguments:
QueueLock - The lock protecting this queue.
CurrentOpIrp - Pointer to a pointer to the currently active
irp for the particular work list. Note that
this item is not actually part of the list.
QueueToProcess - The list to pull the new item off of.
NextIrp - The next Irp to process. Note that CurrentOpIrp
will be set to this value under protection of the
cancel spin lock. However, if *NextIrp is NULL when
this routine returns, it is not necessaryly true the
what is pointed to by CurrentOpIrp will also be NULL.
The reason for this is that if the queue is empty
when we hold the cancel spin lock, a new irp may come
in immediately after we release the lock.
CompleteCurrent - If TRUE then this routine will complete the
irp pointed to by the pointer argument
CurrentOpIrp.
Return Value:
None.
--*/
{
KIRQL oldIrql;
PIRP oldIrp;
KeAcquireSpinLock(
QueueLock,
&oldIrql
);
oldIrp = *CurrentOpIrp;
//
// Check to see if there is a new irp to start up.
//
if (!IsListEmpty(QueueToProcess)) {
PLIST_ENTRY headOfList;
headOfList = RemoveHeadList(QueueToProcess);
*CurrentOpIrp = CONTAINING_RECORD(
headOfList,
IRP,
Tail.Overlay.ListEntry
);
} else {
*CurrentOpIrp = NULL;
}
*NextIrp = *CurrentOpIrp;
KeReleaseSpinLock(
QueueLock,
oldIrql
);
if (CompleteCurrent) {
if (oldIrp) {
IoCompleteRequest(
oldIrp,
IO_SERIAL_INCREMENT
);
}
}
}
NTSTATUS
UniSniffOwnerSettings(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
We manage three things from this level.
1) If asked we change the state so that we are / are not
in passthrough mode.
2) If the owner asks for things that the app won't know about
we note that here.
3) Huh, I was sure there was a third thing.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP for the current request
Return Value:
The function value is the final status of the call
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
ULONG controlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
NTSTATUS status;
//
// For now this test is good enough to figure out if we
// are dealing with the modem state changes.
//
if ((controlCode >> 16) == FILE_DEVICE_MODEM) {
if (controlCode == IOCTL_MODEM_SET_PASSTHROUGH) {
ULONG passThroughType;
//
// Parameter lenght ok?
//
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(ULONG)) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
Irp->IoStatus.Information=0L;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return STATUS_BUFFER_TOO_SMALL;
}
return UniStartOrQueue(
deviceExtension,
&deviceExtension->DeviceLock,
Irp,
&deviceExtension->PassThroughQueue,
&deviceExtension->CurrentPassThrough,
&UniPassThroughStarter
);
} else if (controlCode == IOCTL_MODEM_GET_PASSTHROUGH) {
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(ULONG)) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
Irp->IoStatus.Information=0L;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return STATUS_BUFFER_TOO_SMALL;
}
*(PULONG)Irp->AssociatedIrp.SystemBuffer =
deviceExtension->PassThrough;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof(ULONG);
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return STATUS_SUCCESS;
} else {
//
// Fail the request. Unknown modem command.
//
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Information=0L;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return STATUS_INVALID_PARAMETER;
}
} else {
//
// Not a modem type command. If it is one of the requests that
// set's something that the owner cares about but not apps, then
// record this here.
//
// Is there really such requests? It would seem as though you can't
// tell the difference between another app request and the owner.
//
// BUG BUG For now just pass through.
//
// If it is a mask operation, serialize it.
//
if ((irpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_SERIAL_SET_WAIT_MASK) ||
(irpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_SERIAL_WAIT_ON_MASK)) {
return UniStartOrQueue(
deviceExtension,
&deviceExtension->DeviceLock,
Irp,
&deviceExtension->MaskOps,
&deviceExtension->CurrentMaskOp,
UniMaskStarter
);
} else if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_SERIAL_SET_COMMCONFIG) {
return UniValidateNewCommConfig(
deviceExtension,
Irp,
TRUE
);
} else {
Irp->CurrentLocation++;
Irp->Tail.Overlay.CurrentStackLocation++;
return IoCallDriver(
deviceExtension->AttachedDeviceObject,
Irp
);
}
}
}
NTSTATUS
UniSetupNoPassPart1(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This does the first part of going into no passthrough mode.
It sends down the current irp to the lower level serial driver
with a setmask to clear out the dcd sniff stuff. (Note that
if the client or owner handle still wants to see those changes
these bits will still be set, BUT, the setmask will still be
done which should cause any pending waits to complete.)
Arguments:
DeviceObject - Pointer to the device object for the modem.
Irp - Pointer to the IRP for the current request.
Context - Really a pointer to the Extension.
Return Value:
We ALWAYS return more processing required, when this is actually
called as a completion routine we NEVER want the irp to actually
finish up at this point.
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Irp);
PDEVICE_EXTENSION extension = Context;
KIRQL origIrql;
//
// We don't want anything to change while we test and adjust
// states.
//
KeAcquireSpinLock(
&extension->DeviceLock,
&origIrql
);
if (extension->PassThrough == MODEM_DCDSNIFF) {
//
// We fall out of the dcd sniff state no matter what.
//
extension->PassThrough = MODEM_NOPASSTHROUGH;
UNI_SETUP_NEW_BUFFER(Irp);
nextSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
nextSp->MinorFunction = 0UL;
nextSp->Flags = irpSp->Flags;
nextSp->Parameters.DeviceIoControl.OutputBufferLength = 0UL;
nextSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG);
nextSp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_SET_WAIT_MASK;
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
*((PULONG)Irp->AssociatedIrp.SystemBuffer) =
extension->MaskStates[0].Mask |
extension->MaskStates[1].Mask;
IoSetCompletionRoutine(
Irp,
UniSetupNoPassPart2,
extension,
TRUE,
TRUE,
TRUE
);
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
IoCallDriver(
extension->AttachedDeviceObject,
Irp
);
} else {
//
// Ok, so we weren't in a state where we have to change
// the mask down in the lower serial driver. We still
// have to cause all reads and writes to complete. Call
// the standard routine for doing this. Note that this
// routine is called by the completion routine for the
// mask clearing up above. We simply need to make
// sure that the irp looks like it would after the clearing
// This means that the original systembuffer is saved off.
//
extension->PassThrough = MODEM_NOPASSTHROUGH;
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
UNI_SETUP_NEW_BUFFER(Irp);
UniSetupNoPassPart2(
extension->AttachedDeviceObject,
Irp,
extension
);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
UniSetupNoPassPart2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This does the first part of going into no passthrough mode.
It sends down the current irp to the lower level serial driver
with a setmask to clear out the dcd sniff stuff. (Note that
if the client or owner handle still wants to see those changes
these bits will still be set, BUT, the setmask will still be
done which should cause any pending waits to complete.)
Arguments:
DeviceObject - Pointer to the device object for the modem.
Irp - Pointer to the IRP for the current request.
Context - Really a pointer to the Extension.
Return Value:
Always more processing required since we don't want
the iosubsystem to mess with this irp.
--*/
{
PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Irp);
nextSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
nextSp->MinorFunction = 0UL;
nextSp->Flags = 0;
nextSp->Parameters.DeviceIoControl.OutputBufferLength = 0UL;
nextSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG);
nextSp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_PURGE;
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
*((PULONG)Irp->AssociatedIrp.SystemBuffer) =
SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT;
IoSetCompletionRoutine(
Irp,
UniSetupNoPassPart3,
Context,
TRUE,
TRUE,
TRUE
);
IoCallDriver(
((PDEVICE_EXTENSION)Context)->AttachedDeviceObject,
Irp
);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
UniSetupNoPassPart3(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This is called when all the no passthrough code has reached
completion.
We have to irps that we could have been called with.
1) An irp from above that actually put us into a nopassthrough
state.
2) The modem driver initiated wait completing.
Arguments:
DeviceObject - Pointer to the device object for the modem.
Irp - Pointer to the IRP for the current request.
Context - Really a pointer to the Extension.
Return Value:
Always more processing required since we don't want
the iosubsystem to mess with this irp.
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION extension = Context;
//
// How do we tell caller initiated nopassthrough from modem driver
// wait completion (since we STOMPED on the ioctl value)? The
// modem drivers saved off systembuffer will be null. We know that
// the caller initiated saved systembuffer HAD to be non-null cause
// it would have NEVER made it past our irp validation code.
//
if (UNI_ORIG_SYSTEM_BUFFER(Irp)) {
PIRP newIrp;
//
// app initiated irp. Put back the old system buffer and
// restore the ioctl.
//
UNI_RESTORE_IRP(
Irp,
IOCTL_MODEM_SET_PASSTHROUGH
);
//
// Now, we are going to return more processing because we are
// going to call our regular queue processing code
// to process the passthrough queue. The queue processing
// code will actually reinvoke complete on this irp.
//
UniGetNextIrp(
&extension->DeviceLock,
&extension->CurrentPassThrough,
&extension->PassThroughQueue,
&newIrp,
TRUE
);
if (newIrp) {
UniPassThroughStarter(extension);
}
return STATUS_MORE_PROCESSING_REQUIRED;
} else {
//
// The modem drivers wait is all done.
//
// We need to see if there are shuttled wait operations to
// be sent down.
//
// The basic theory here is that we take out the device
// lock, look to see if we are no longer in sniff mode.
// If this is the case then try to send to a shuttled wait
// that is suitable for sending down. If the shuttled
// wait makes it on down, it might already find a wait
// already there because a dcd sniff came in. That's ok.
// Shuttled waits that are sent down can handle being completed
// with an error (they just reshuttle).
//
KIRQL origIrql;
KeAcquireSpinLock(
&extension->DeviceLock,
&origIrql
);
if (extension->PassThrough != MODEM_DCDSNIFF) {
if (!(extension->MaskStates[0].PassedDownWait ||
extension->MaskStates[1].PassedDownWait)) {
if (extension->MaskStates[0].ShuttledWait) {
UniChangeShuttledToPassDown(
&extension->MaskStates[0],
origIrql
);
} else if (extension->MaskStates[1].ShuttledWait) {
UniChangeShuttledToPassDown(
&extension->MaskStates[1],
origIrql
);
} else {
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
}
} else {
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
}
} else {
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
}
NTSTATUS
UniPassThroughStarter(
IN PDEVICE_EXTENSION Extension
)
{
NTSTATUS firstStatus;
BOOLEAN setFirstStatus = FALSE;
PIRP newIrp;
PIRP irp;
ULONG passThroughType;
do {
irp = Extension->CurrentPassThrough;
passThroughType = *(PULONG)irp->AssociatedIrp.SystemBuffer;
if (passThroughType == MODEM_NOPASSTHROUGH) {
//
// Requested to go into the not connected (no passthrough)
// state.
//
// Change the state to nopassthrough. When we are done with
// that, purge the read/write data (not the hardware buffers
// though).
//
//
// If we are already in the nopassthrough, then nothing
// to do.
//
if (Extension->PassThrough == MODEM_NOPASSTHROUGH) {
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = 0L;
} else {
//
// The following will actually start off the
// work of putting us into passthrough mode.
//
// Since this ALWAYS entails calling down to a
// lower level driver we know we won't be completing
// and that we won't be starting a new irp right
// away, so we can just return.
//
irp->IoStatus.Status = STATUS_PENDING;
IoMarkIrpPending(irp);
UniSetupNoPassPart1(
Extension->DeviceObject,
irp,
Extension
);
if (!setFirstStatus) {
return STATUS_PENDING;
} else {
return firstStatus;
}
}
} else if (passThroughType == MODEM_PASSTHROUGH) {
//
// Set into the passthrough state. Make sure that any
// owner settings and app settings are still set up and
// set passthrough.
//
//
// If already in this state then do nothing.
//
if (Extension->PassThrough != MODEM_PASSTHROUGH) {
//
// If going to this state from DCD sniffing state, kill the
// wait (resubmit if there is any reason from the apps).
//
irp->IoStatus.Status = STATUS_PENDING;
IoMarkIrpPending(irp);
UniSetupPass(
Extension->DeviceObject,
irp,
Extension
);
if (!setFirstStatus) {
return STATUS_PENDING;
} else {
return firstStatus;
}
} else {
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = 0L;
}
} else if (passThroughType == MODEM_DCDSNIFF) {
//
// Go into the connect (passthrough) state with dcd sniffing.
//
// Any wait requests sent down by the apps were filtered so
// and were replaced by our own wait. Set a new mask with
// our dcd bit. This will cause the current wait to finish.
// resubmit it with dcd sniffing turned on.
//
if (Extension->PassThrough != MODEM_DCDSNIFF) {
//
// We call the routine that starts off the sniffing.
// Since this work inherintly calls lower level serial
// drivers we know that this will pend. If this is
// the first time through the loop make sure we return
// pending.
//
irp->IoStatus.Status = STATUS_PENDING;
IoMarkIrpPending(irp);
UniSetupSniffPart0(
Extension->DeviceObject,
irp,
Extension
);
if (!setFirstStatus) {
return STATUS_PENDING;
} else {
return firstStatus;
}
} else {
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = 0L;
}
} else {
//
// Fail the request. Unknown modem command.
//
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
irp->IoStatus.Information=0L;
}
if (!setFirstStatus) {
firstStatus = irp->IoStatus.Status;
setFirstStatus = TRUE;
}
UniGetNextIrp(
&Extension->DeviceLock,
&Extension->CurrentPassThrough,
&Extension->PassThroughQueue,
&newIrp,
TRUE
);
} while (newIrp);
return firstStatus;
}
NTSTATUS
UniSetupPass(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This sets us up for passthrough IF we are called while DCD
sniffing enabled. The basic theory here is that by
sending down the setmask that will wipe out the pending
wait (and completing the pending wait will take care of
all it's gory details itself). We just send down the setmask
and set the completion routine to the nopassthrough final
completion cause that has code to restore the irps and
start off a new one.
Arguments:
DeviceObject - Pointer to the device object for the modem.
Irp - Pointer to the IRP for the current request.
Context - Really a pointer to the Extension.
Return Value:
We ALWAYS return more processing required.
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Irp);
PDEVICE_EXTENSION extension = Context;
KIRQL origIrql;
//
// We don't want anything to change while we test and adjust
// states.
//
KeAcquireSpinLock(
&extension->DeviceLock,
&origIrql
);
if (extension->PassThrough == MODEM_DCDSNIFF) {
//
// We fall out of the dcd sniff state no matter what.
//
extension->PassThrough = MODEM_PASSTHROUGH;
UNI_SETUP_NEW_BUFFER(Irp);
nextSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
nextSp->MinorFunction = 0UL;
nextSp->Flags = irpSp->Flags;
nextSp->Parameters.DeviceIoControl.OutputBufferLength = 0UL;
nextSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG);
nextSp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_SET_WAIT_MASK;
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
*((PULONG)Irp->AssociatedIrp.SystemBuffer) =
extension->MaskStates[0].Mask |
extension->MaskStates[1].Mask;
IoSetCompletionRoutine(
Irp,
UniSetupNoPassPart3,
extension,
TRUE,
TRUE,
TRUE
);
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
IoCallDriver(
extension->AttachedDeviceObject,
Irp
);
} else {
//
// Ok, so we weren't in a state where we have to change
// the mask down in the lower serial driver. (Something
// came in around us?) Simply finish up the irp.
//
extension->PassThrough = MODEM_PASSTHROUGH;
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0UL;
UNI_SETUP_NEW_BUFFER(Irp);
UniSetupNoPassPart3(
extension->AttachedDeviceObject,
Irp,
extension
);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
UniSetupSniffPart0(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This begins setting up for dcd sniffing. We KNOW
getting here we weren't already in dcd sniff.
1) Set the passthrough state to dcdsniff.
2) Call down to the lower serial driver to set the mask to
0. This will totally clear out the state.
3) Further processing (in the completion) will send down a new
setmask as well as a wait irp that has been preallocated by
the modem driver.
Arguments:
DeviceObject - Pointer to the device object for the modem.
Irp - Pointer to the IRP for the current request.
Context - Really a pointer to the Extension.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Irp);
PDEVICE_EXTENSION extension = Context;
extension->PassThrough = MODEM_DCDSNIFF;
UNI_SETUP_NEW_BUFFER(Irp);
nextSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
nextSp->MinorFunction = 0UL;
nextSp->Flags = irpSp->Flags;
nextSp->Parameters.DeviceIoControl.OutputBufferLength = 0UL;
nextSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG);
nextSp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_SET_WAIT_MASK;
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
*((PULONG)Irp->AssociatedIrp.SystemBuffer) = 0;
IoSetCompletionRoutine(
Irp,
UniSetupSniffPart1,
extension,
TRUE,
TRUE,
TRUE
);
IoCallDriver(
extension->AttachedDeviceObject,
Irp
);
return STATUS_SUCCESS;
}
NTSTATUS
UniSetupSniffPart1(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
Call down to the lower serial driver to set the mask to
sniff for DCD.
Further processing (in the completion) will send down a wait irp
that has been preallocated by the modem driver.
Arguments:
DeviceObject - Pointer to the device object for the modem.
Irp - Pointer to the IRP for the current request.
Context - Really a pointer to the Extension.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Irp);
PDEVICE_EXTENSION extension = Context;
if (NT_ERROR(Irp->IoStatus.Status)) {
PIRP newIrp = NULL;
extension->PassThrough = MODEM_PASSTHROUGH;
UNI_RESTORE_IRP(
Irp,
IOCTL_MODEM_SET_PASSTHROUGH
);
UniGetNextIrp(
&extension->DeviceLock,
&extension->CurrentPassThrough,
&extension->PassThroughQueue,
&newIrp,
TRUE
);
if (newIrp) {
UniPassThroughStarter(extension);
}
} else {
nextSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
nextSp->MinorFunction = 0UL;
nextSp->Flags = irpSp->Flags;
nextSp->Parameters.DeviceIoControl.OutputBufferLength = 0UL;
nextSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG);
nextSp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_SET_WAIT_MASK;
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
*((PULONG)Irp->AssociatedIrp.SystemBuffer) =
extension->MaskStates[0].Mask |
extension->MaskStates[1].Mask |
(SERIAL_EV_RLSD | SERIAL_EV_DSR);
IoSetCompletionRoutine(
Irp,
UniSetupSniffPart2,
extension,
TRUE,
TRUE,
TRUE
);
IoCallDriver(
extension->AttachedDeviceObject,
Irp
);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
UniSetupSniffPart2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This ends setting up for dcd sniffing. If the status is actually
ok, then send down the wait. Otherwise go into passthrough mode.
Arguments:
DeviceObject - Pointer to the device object for the modem.
Irp - Pointer to the IRP for the current request.
Context - Really a pointer to the Extension.
Return Value:
Always more processing required. We want to call our
regular queue processing code at this point. It will
actually complete the irp.
--*/
{
PDEVICE_EXTENSION extension = Context;
PIRP newIrp;
if (NT_ERROR(Irp->IoStatus.Status)) {
extension->PassThrough = MODEM_PASSTHROUGH;
} else {
UniSendOurWaitDown(extension);
}
UNI_RESTORE_IRP(
Irp,
IOCTL_MODEM_SET_PASSTHROUGH
);
UniGetNextIrp(
&extension->DeviceLock,
&extension->CurrentPassThrough,
&extension->PassThroughQueue,
&newIrp,
TRUE
);
if (newIrp) {
UniPassThroughStarter(extension);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
UniSniffWaitComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This is the completion routine associated with the wait irp
owned by the serial driver.
Arguments:
DeviceObject - Pointer to the device object for the modem.
Irp - Pointer to the IRP for the current request.
Context - Really a pointer to the Extension.
Return Value:
Always more processing required. This is a driver owned irp.
We NEVER want the io subsystem to automatically deallocate it.
--*/
{
PDEVICE_EXTENSION extension = Context;
PIRP newIrp;
ULONG maskValue;
KIRQL origIrql;
//
// We can complete because
//
// 1) The irp was cancelled.
//
// 2) The irp was in error.
//
// 3) The wait is satisfied.
//
// Let's examine each case separately.
//
// Case 1:
//
// The irp can only be cancelled by the modem driver itself
// because the modem device is being closed. (This is
// because nobody but the modem driver knows this irp
// exists (it's not in any threadlist.)
//
// In this case, we should just act like we saw a DCD
// change. This will cause everything to get shut down.
// This is just the shape that we want to be in for
// closing.
//
// Case 2:
//
// Somehow, some other wait go in ahead of us. It seems
// as though the only reasonable course of action here is
// to go into nopassthrough again. This shuts everything
// down, and can put us into a state that the upper applications
// can detect an move forward with. Note that I can not
// envision a situation where we would get an error. This
// is why we will have an assert for this case.
//
// Case 3:
//
// There are 3 subcases here.
//
// Case A:
//
// The irp completes with a mask value of zero. This implies
// that somebody sent down a new setmask. (A client changes
// what they are looking for perhaps.) In this case
// if we are still in DCD sniff mode, then we should simply
// resubmit ourselves to the lower serial driver. If we are
// no longer in sniff mode then we look for a shuttled wait.
// if there is one, send it on down.
//
// Case B:
//
// The irp completes with a mask value that doesn't include
// the DCD sniff values. This means that we need to look
// at each mask state and complete an irp if waiting
// for that kind of event (or update it's history if no wait
// shuttled).
//
// Case C:
//
// The irp completes with a mask value the does include
// the DCD sniff values. Essentially at this point we need
// to go into nopassthrough mode. Do all the stuff associated
// with that. Then we need to essentially do what is in
// case 3B, and complete any irps there that are waiting
// on returned events.
//
ASSERT(NT_SUCCESS(Irp->IoStatus.Status) ||
Irp->IoStatus.Status == STATUS_CANCELLED);
//
// Freeze everything up. We don't want anybody to move
// now that we are thinking about who to complete and
// send down.
//
KeAcquireSpinLock(
&extension->DeviceLock,
&origIrql
);
if (!NT_SUCCESS(Irp->IoStatus.Status) ||
Irp->IoStatus.Status == STATUS_CANCELLED) {
//
// Set up so that we fall out of dcd sniff mode as well
// as complete any pending waits.
//
maskValue = ~0UL;
} else {
maskValue = *((PULONG)Irp->AssociatedIrp.SystemBuffer);
}
if (!maskValue) {
//
// Case A.
//
if (extension->PassThrough == MODEM_DCDSNIFF) {
//
// Send ourself back down.
//
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
UniSendOurWaitDown(extension);
} else {
//
// The following sees if there is a shuttled wait. If there
// is it will send it down. There isn't any real possiblity
// of starving a shuttled wait, as app wait completion code
// will always complete both waits if possible.
//
// As a note, the set mask that caused us to complete with
// a maskvalue of zero, CAN NOT cause the client or owner
// shuttled waits to complete **HERE** because if the setmask
// originated from the client or owner, it would have completed
// the shuttled wait in it's ordinary processing before it
// was sent down to the lower level serial driver.
//
if (!(extension->MaskStates[0].PassedDownWait ||
extension->MaskStates[1].PassedDownWait)) {
if (extension->MaskStates[0].ShuttledWait) {
UniChangeShuttledToPassDown(
&extension->MaskStates[0],
origIrql
);
} else if (extension->MaskStates[1].ShuttledWait) {
UniChangeShuttledToPassDown(
&extension->MaskStates[1],
origIrql
);
} else {
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
}
} else {
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
}
}
} else if (maskValue & (SERIAL_EV_RLSD | SERIAL_EV_DSR)) {
//
// Got something for the DCD sniff.
//
// We first call up the code to go into the no passthrough mode.
// That code does NOT take care of finding a shuttled wait to
// pass down. We will do that in the completion routine.
//
//
MASKSTATE maskStates[2] = {0};
UniPreProcessShuttledWaits(
extension,
&maskStates[0],
maskValue
);
//
// Re-init the wait apps system buffer to null since we
// will completely reuse the irp in the following pass.
//
Irp->AssociatedIrp.SystemBuffer = NULL;
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
UniSetupNoPassPart1(
DeviceObject,
Irp,
Context
);
UniPostProcessShuttledWaits(
extension,
&maskStates[0],
maskValue
);
} else {
//
// This is a case where the dcd sniff isn't satisfied BUT, there
// is a bit that a client/owner has asked to be able to wait on.
// Resubmit ourself back down, but also update the client/owner
// wait operations.
//
MASKSTATE maskStates[2] = {0};
UniPreProcessShuttledWaits(
extension,
&maskStates[0],
maskValue
);
KeReleaseSpinLock(
&extension->DeviceLock,
origIrql
);
UniSendOurWaitDown(extension);
UniPostProcessShuttledWaits(
extension,
&maskStates[0],
maskValue
);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
UniSendOurWaitDown(
IN PDEVICE_EXTENSION Extension
)
/*++
Routine Description:
Sends the wait operation down to the lower level serial driver
Arguments:
Extension - The device extension of the modem device
Return Value:
None.
--*/
{
PIRP irp = Extension->OurWaitIrp;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(irp);
irp->AssociatedIrp.SystemBuffer = NULL;
UNI_SETUP_NEW_BUFFER(irp);
nextSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
nextSp->MinorFunction = 0;
nextSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ULONG);
nextSp->Parameters.DeviceIoControl.InputBufferLength = 0UL;;
nextSp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_WAIT_ON_MASK;
nextSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
irp->CancelIrql = 0;
irp->CancelRoutine = NULL;
irp->Cancel = FALSE;
IoSetCompletionRoutine(
Extension->OurWaitIrp,
UniSniffWaitComplete,
Extension,
TRUE,
TRUE,
TRUE
);
IoCallDriver(
Extension->AttachedDeviceObject,
Extension->OurWaitIrp
);
}
VOID
UniPreProcessShuttledWaits(
IN PDEVICE_EXTENSION Extension,
IN PMASKSTATE MaskStates,
IN ULONG MaskValue
)
/*++
Routine Description:
This routine will go through shuttled waits and see which
ones can be satisifed with the passed in mask value. Any that
would be completable are pulled out of the device extension.
Any history masks are updated also.
NOTE: This routine is called with the lock held.
Arguments:
Extension - The device extension of the modem device
MaskStates - Points to the first element of a maskstate array.
MaskValue - The mask that the items would be completed with.
Return Value:
None.
--*/
{
ULONG index = 0;
if (Extension->MaskStates[0].Mask & MaskValue) {
if (Extension->MaskStates[0].ShuttledWait) {
MaskStates[index] = Extension->MaskStates[0];
Extension->MaskStates[0].ShuttledWait = NULL;
MaskStates[index].OtherState = &Extension->MaskStates[0];
index = 1;
} else {
Extension->MaskStates[0].HistoryMask |=
Extension->MaskStates[0].Mask & MaskValue;
}
}
if (Extension->MaskStates[1].Mask & MaskValue) {
if (Extension->MaskStates[1].ShuttledWait) {
MaskStates[index] = Extension->MaskStates[1];
Extension->MaskStates[1].ShuttledWait = NULL;
MaskStates[index].OtherState = &Extension->MaskStates[1];
} else {
Extension->MaskStates[1].HistoryMask |=
Extension->MaskStates[1].Mask & MaskValue;
}
}
}
VOID
UniPostProcessShuttledWaits(
IN PDEVICE_EXTENSION Extension,
IN PMASKSTATE MaskStates,
IN ULONG MaskValue
)
/*++
Routine Description:
This routine will take any preprocessed shuttled waits and run them
down
Arguments:
Extension - The device extension of the modem device
MaskStates - Points to the first element of a maskstate array.
MaskValue - The mask used in completing the operation.
Return Value:
None.
--*/
{
KIRQL origIrql;
PIRP irpToComplete = MaskStates[0].ShuttledWait;
if (irpToComplete) {
//
// Initiated sending the wait down. Rundown any waits that
// we should have satisfied.
//
MaskStates[0].ShuttledWait = NULL;
KeAcquireSpinLock(
&Extension->DeviceLock,
&origIrql
);
UniRundownShuttledWait(
Extension,
&MaskStates[0].ShuttledWait,
UNI_REFERENCE_NORMAL_PATH,
irpToComplete,
origIrql,
STATUS_SUCCESS,
(ULONG)MaskStates[0].Mask & MaskValue
);
irpToComplete = MaskStates[1].ShuttledWait;
if (irpToComplete) {
MaskStates[1].ShuttledWait = NULL;
KeAcquireSpinLock(
&Extension->DeviceLock,
&origIrql
);
UniRundownShuttledWait(
Extension,
&MaskStates[1].ShuttledWait,
UNI_REFERENCE_NORMAL_PATH,
irpToComplete,
origIrql,
STATUS_SUCCESS,
(ULONG)MaskStates[1].Mask & MaskValue
);
}
}
}
NTSTATUS
UniValidateNewCommConfig(
IN PDEVICE_EXTENSION Extension,
IN PIRP Irp,
IN BOOLEAN Owner
)
/*++
Routine Description:
Validates that new comm config settings do NOT conflict
with the devcaps.
Arguments:
Extension - The device extension of the modem device
Irp - The irp with the new settings.
Return Value:
STATUS_BUFFER_TOO_SMALL if not enough passed for the settings,
STATUS_SUCCESS otherwise.
--*/
{
#define MIN_CALL_SETUP_FAIL_TIMER 1
#define MIN_INACTIVITY_TIMEOUT 0
KIRQL origIrql;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
LPCOMMCONFIG localConf = (LPCOMMCONFIG)(Irp->AssociatedIrp.SystemBuffer);
PMODEMSETTINGS localSet = (PVOID)&localConf->wcProviderData[0];
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(MODEMSETTINGS)) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
Irp->IoStatus.Information = 0L;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return STATUS_BUFFER_TOO_SMALL;
}
//
// Acquire the spinlock now while we change settings.
//
KeAcquireSpinLock(
&Extension->DeviceLock,
&origIrql
);
if (localSet->dwCallSetupFailTimer >
Extension->ModemDevCaps.dwCallSetupFailTimer) {
Extension->ModemSettings.dwCallSetupFailTimer =
Extension->ModemDevCaps.dwCallSetupFailTimer;
} else if (localSet->dwCallSetupFailTimer < MIN_CALL_SETUP_FAIL_TIMER) {
Extension->ModemSettings.dwCallSetupFailTimer =
MIN_CALL_SETUP_FAIL_TIMER;
} else {
Extension->ModemSettings.dwCallSetupFailTimer =
localSet->dwCallSetupFailTimer;
}
if (localSet->dwInactivityTimeout >
Extension->ModemDevCaps.dwInactivityTimeout) {
Extension->ModemSettings.dwInactivityTimeout =
Extension->ModemDevCaps.dwInactivityTimeout;
} else if (localSet->dwInactivityTimeout < MIN_INACTIVITY_TIMEOUT) {
Extension->ModemSettings.dwInactivityTimeout =
MIN_INACTIVITY_TIMEOUT;
} else {
Extension->ModemSettings.dwInactivityTimeout =
localSet->dwInactivityTimeout;
}
if ((1 << localSet->dwSpeakerVolume) &
Extension->ModemDevCaps.dwSpeakerVolume) {
Extension->ModemSettings.dwSpeakerVolume = localSet->dwSpeakerVolume;
}
if ((1 << localSet->dwSpeakerMode) &
Extension->ModemDevCaps.dwSpeakerMode) {
Extension->ModemSettings.dwSpeakerMode = localSet->dwSpeakerMode;
}
Extension->ModemSettings.dwPreferredModemOptions =
localSet->dwPreferredModemOptions &
Extension->ModemDevCaps.dwModemOptions;
//
// The owner is allowed to set these fields and we do not need
// to question their validity. It is the owner's responsibility.
//
if (Owner) {
Extension->ModemSettings.dwNegotiatedModemOptions =
localSet->dwNegotiatedModemOptions;
Extension->ModemSettings.dwNegotiatedDCERate =
localSet->dwNegotiatedDCERate;
}
KeReleaseSpinLock(
&Extension->DeviceLock,
origIrql
);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return STATUS_SUCCESS;
}