📄 write.c
字号:
is that while the cancel lock is held the write
queue ended up being empty, but as soon as we release
the cancel spin lock a new irp came in from
SerialStartWrite.
CompleteCurrent - Flag indicates whether the CurrentOpIrp should
be completed.
Return Value:
None.
--*/
{
// PSERIAL_DEVICE_EXTENSION Extension = CONTAINING_RECORD(
// QueueToProcess,
// SERIAL_DEVICE_EXTENSION,
// WriteQueue
// );
SERIAL_LOCKED_PAGED_CODE();
SerialDump(SERTRACECALLS, ("SERIAL: SerialGetNextWrite\n"));
do {
//
// We could be completing a flush.
//
if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
== IRP_MJ_WRITE) {
KIRQL OldIrql;
ASSERT(Extension->TotalCharsQueued >=
(IoGetCurrentIrpStackLocation(*CurrentOpIrp)
->Parameters.Write.Length));
IoAcquireCancelSpinLock(&OldIrql);
Extension->TotalCharsQueued -=
IoGetCurrentIrpStackLocation(*CurrentOpIrp)
->Parameters.Write.Length;
IoReleaseCancelSpinLock(OldIrql);
} else if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
== IRP_MJ_DEVICE_CONTROL) {
KIRQL OldIrql;
PIRP Irp;
PSERIAL_XOFF_COUNTER Xc;
IoAcquireCancelSpinLock(&OldIrql);
Irp = *CurrentOpIrp;
Xc = Irp->AssociatedIrp.SystemBuffer;
//
// We should never have a xoff counter when we
// get to this point.
//
ASSERT(!Extension->CurrentXoffIrp);
//
// We absolutely shouldn't have a cancel routine
// at this point.
//
ASSERT(!Irp->CancelRoutine);
//
// This could only be a xoff counter masquerading as
// a write irp.
//
Extension->TotalCharsQueued--;
//
// Check to see of the xoff irp has been set with success.
// This means that the write completed normally. If that
// is the case, and it hasn't been set to cancel in the
// meanwhile, then go on and make it the CurrentXoffIrp.
//
if (Irp->IoStatus.Status != STATUS_SUCCESS) {
//
// Oh well, we can just finish it off.
//
NOTHING;
} else if (Irp->Cancel) {
Irp->IoStatus.Status = STATUS_CANCELLED;
} else {
//
// Give it a new cancel routine, and increment the
// reference count because the cancel routine has
// a reference to it.
//
IoSetCancelRoutine(
Irp,
SerialCancelCurrentXoff
);
SERIAL_SET_REFERENCE(
Irp,
SERIAL_REF_CANCEL
);
//
// We don't want to complete the current irp now. This
// will now get completed by the Xoff counter code.
//
CompleteCurrent = FALSE;
//
// Give the counter to the isr.
//
Extension->CurrentXoffIrp = Irp;
KeSynchronizeExecution(
Extension->Interrupt,
SerialGiveXoffToIsr,
Extension
);
//
// Start the timer for the counter and increment
// the reference count since the timer has a
// reference to the irp.
//
if (Xc->Timeout) {
LARGE_INTEGER delta;
delta.QuadPart = -((LONGLONG)UInt32x32To64(
1000,
Xc->Timeout
));
SerialSetTimer(
&Extension->XoffCountTimer,
delta,
&Extension->XoffCountTimeoutDpc,
Extension
);
SERIAL_SET_REFERENCE(
Irp,
SERIAL_REF_TOTAL_TIMER
);
}
}
IoReleaseCancelSpinLock(OldIrql);
}
//
// Note that the following call will (probably) also cause
// the current irp to be completed.
//
SerialGetNextIrp(
CurrentOpIrp,
QueueToProcess,
NewIrp,
CompleteCurrent,
Extension
);
if (!*NewIrp) {
KIRQL OldIrql;
IoAcquireCancelSpinLock(&OldIrql);
KeSynchronizeExecution(
Extension->Interrupt,
SerialProcessEmptyTransmit,
Extension
);
IoReleaseCancelSpinLock(OldIrql);
break;
} else if (IoGetCurrentIrpStackLocation(*NewIrp)->MajorFunction
== IRP_MJ_FLUSH_BUFFERS) {
//
// If we encounter a flush request we just want to get
// the next irp and complete the flush.
//
// Note that if NewIrp is non-null then it is also
// equal to CurrentWriteIrp.
//
ASSERT((*NewIrp) == (*CurrentOpIrp));
(*NewIrp)->IoStatus.Status = STATUS_SUCCESS;
} else {
break;
}
} while (TRUE);
}
VOID
SerialCompleteWrite(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This routine is merely used to complete any write. It
assumes that the status and the information fields of
the irp are already correctly filled in.
Arguments:
Dpc - Not Used.
DeferredContext - Really points to the device extension.
SystemContext1 - Not Used.
SystemContext2 - Not Used.
Return Value:
None.
--*/
{
PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
KIRQL OldIrql;
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
SerialDump(SERTRACECALLS, ("SERIAL: SerialCompleteWrite\n"));
IoAcquireCancelSpinLock(&OldIrql);
SerialTryToCompleteCurrent(
Extension,
NULL,
OldIrql,
STATUS_SUCCESS,
&Extension->CurrentWriteIrp,
&Extension->WriteQueue,
NULL,
&Extension->WriteRequestTotalTimer,
SerialStartWrite,
SerialGetNextWrite,
SERIAL_REF_ISR
);
SerialDpcEpilogue(Extension, Dpc);
}
BOOLEAN
SerialProcessEmptyTransmit(
IN PVOID Context
)
/*++
Routine Description:
This routine is used to determine if conditions are appropriate
to satisfy a wait for transmit empty event, and if so to complete
the irp that is waiting for that event. It also call the code
that checks to see if we should lower the RTS line if we are
doing transmit toggling.
NOTE: This routine is called by KeSynchronizeExecution.
NOTE: This routine assumes that it is called with the cancel
spinlock held.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
This routine always returns FALSE.
--*/
{
PSERIAL_DEVICE_EXTENSION Extension = Context;
SERIAL_LOCKED_PAGED_CODE();
if (Extension->IsrWaitMask && (Extension->IsrWaitMask & SERIAL_EV_TXEMPTY) &&
Extension->EmptiedTransmit && (!Extension->TransmitImmediate) &&
(!Extension->CurrentWriteIrp) && IsListEmpty(&Extension->WriteQueue)) {
Extension->HistoryMask |= SERIAL_EV_TXEMPTY;
if (Extension->IrpMaskLocation) {
*Extension->IrpMaskLocation = Extension->HistoryMask;
Extension->IrpMaskLocation = NULL;
Extension->HistoryMask = 0;
Extension->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
SerialInsertQueueDpc(
&Extension->CommWaitDpc,
NULL,
NULL,
Extension
);
}
Extension->CountOfTryingToLowerRTS++;
SerialPerhapsLowerRTS(Extension);
}
return FALSE;
}
BOOLEAN
SerialGiveWriteToIsr(
IN PVOID Context
)
/*++
Routine Description:
Try to start off the write by slipping it in behind
a transmit immediate char, or if that isn't available
and the transmit holding register is empty, "tickle"
the UART into interrupting with a transmit buffer
empty.
NOTE: This routine is called by KeSynchronizeExecution.
NOTE: This routine assumes that it is called with the
cancel spin lock held.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
This routine always returns FALSE.
--*/
{
PSERIAL_DEVICE_EXTENSION Extension = Context;
//
// The current stack location. This contains all of the
// information we need to process this particular request.
//
PIO_STACK_LOCATION IrpSp;
SERIAL_LOCKED_PAGED_CODE();
IrpSp = IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp);
//
// We might have a xoff counter request masquerading as a
// write. The length of these requests will always be one
// and we can get a pointer to the actual character from
// the data supplied by the user.
//
if (IrpSp->MajorFunction == IRP_MJ_WRITE) {
Extension->WriteLength = IrpSp->Parameters.Write.Length;
Extension->WriteCurrentChar =
Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer;
} else {
Extension->WriteLength = 1;
Extension->WriteCurrentChar =
((PUCHAR)Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer) +
FIELD_OFFSET(
SERIAL_XOFF_COUNTER,
XoffChar
);
}
//
// The isr now has a reference to the irp.
//
SERIAL_SET_REFERENCE(
Extension->CurrentWriteIrp,
SERIAL_REF_ISR
);
//
// Check first to see if an immediate char is transmitting.
// If it is then we'll just slip in behind it when its
// done.
//
if (!Extension->TransmitImmediate) {
//
// If there is no immediate char transmitting then we
// will "re-enable" the transmit holding register empty
// interrupt. The 8250 family of devices will always
// signal a transmit holding register empty interrupt
// *ANY* time this bit is set to one. By doing things
// this way we can simply use the normal interrupt code
// to start off this write.
//
// We've been keeping track of whether the transmit holding
// register is empty so it we only need to do this
// if the register is empty.
//
if (Extension->HoldingEmpty) {
DISABLE_ALL_INTERRUPTS(Extension->Controller);
ENABLE_ALL_INTERRUPTS(Extension->Controller);
}
}
//
// The rts line may already be up from previous writes,
// however, it won't take much additional time to turn
// on the RTS line if we are doing transmit toggling.
//
if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
SERIAL_TRANSMIT_TOGGLE) {
SerialSetRTS(Extension);
}
return FALSE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -