📄 write.c
字号:
/**************** XXX
Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
Copyright (c) 2000 Maverick Research Inc. Pte Ltd
Module Name:
write.c
Abstract:
This module contains the code that is very specific to write
operations in the serial driver
Author:
Anthony V. Ercolano 26-Sep-1991
Henry Tan 18-Aug-2000
Environment:
Kernel mode
--*/
//#include "precomp.h"
#include "wdm.h"
#include "stdarg.h"
#include "stdio.h"
#include "usbdi.h"
#include "usbdlib.h"
#include "usbcom.h"
BOOLEAN
SerialGiveWriteToIsr(
IN PVOID Context
);
VOID
SerialCancelCurrentWrite(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
);
BOOLEAN
SerialGrabWriteFromIsr(
IN PVOID Context
);
BOOLEAN
SerialGrabXoffFromIsr(
IN PVOID Context
);
VOID
SerialCancelCurrentXoff(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
);
BOOLEAN
SerialGiveXoffToIsr(
IN PVOID Context
);
/*
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGESER,SerialProcessEmptyTransmit)
#pragma alloc_text(PAGESER,SerialWrite)
#pragma alloc_text(PAGESER,SerialStartWrite)
#pragma alloc_text(PAGESER,SerialGetNextWrite)
#pragma alloc_text(PAGESER,SerialGiveWriteToIsr)
#pragma alloc_text(PAGESER,SerialCancelCurrentWrite)
#pragma alloc_text(PAGESER,SerialGrabWriteFromIsr)
#pragma alloc_text(PAGESER,SerialGrabXoffFromIsr)
#pragma alloc_text(PAGESER,SerialCancelCurrentXoff)
#pragma alloc_text(PAGESER,SerialGiveXoffToIsr)
#endif
*/
NTSTATUS
SerialWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This is the dispatch routine for write. It validates the parameters
for the write request and if all is ok then it places the request
on the work queue.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP for the current request
Return Value:
If the io is zero length then it will return STATUS_SUCCESS,
otherwise this routine will return STATUS_PENDING.
--*/
{
PDEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
NTSTATUS status;
//SERIAL_LOCKED_PAGED_CODE();
//SerialDump(SERTRACECALLS, ("Entering SerialWrite\n"));
// Can't accept a new io request if:
// 1) device is removed,
// 2) has never been started,
// 3) is stopped,
// 4) has a remove request pending,
// 5) has a stop device pending
if ( !UsbCom_CanAcceptIoRequests(DeviceObject) ) {
status = STATUS_DELETE_PENDING;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
DbgPrint("SerialWrite: Removed: %d, Started: %d, Stop: %d\n",
Extension->DeviceRemoved,
Extension->DeviceStarted,
Extension->StopDeviceRequested);
return status;
}
UsbCom_IncrementIoCount(DeviceObject);
/*
if ((status = SerialIRPPrologue(Irp, Extension)) != STATUS_SUCCESS) {
SerialCompleteRequest(Extension, Irp, IO_NO_INCREMENT);
SerialDump(SERTRACECALLS, ("Leaving SerialWrite (1)\n"));
return status;
}
SerialDump(
SERIRPPATH,
("SERIAL: Dispatch entry for: %x\n",Irp)
);
if (SerialCompleteIfError(
DeviceObject,
Irp
) != STATUS_SUCCESS) {
SerialDump(SERTRACECALLS, ("Leaving SerialWrite (2)\n"));
return STATUS_CANCELLED;
}
*/
Irp->IoStatus.Information = 0L;
//
// Quick check for a zero length write. If it is zero length
// then we are already done!
//
if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length) {
//
// Well it looks like we actually have to do some
// work. Put the write on the queue so that we can
// process it when our previous writes are done.
//
// SerialDump(SERTRACECALLS, ("Leaving SerialWrite (3)\n"));
return SerialStartOrQueue(
Extension,
Irp,
&Extension->WriteQueue,
&Extension->CurrentWriteIrp,
SerialStartWrite
);
} else {
Irp->IoStatus.Status = STATUS_SUCCESS;
//SerialDump(
// SERIRPPATH,
//("SERIAL: DO=%08x Complete Irp: %x\n",DeviceObject, Irp)
//);
SerialCompleteRequest(Extension, Irp, 0);
//SerialDump(SERTRACECALLS, ("Leaving SerialWrite (4)\n"));
return STATUS_SUCCESS;
}
}
NTSTATUS
SerialStartWrite(
IN PDEVICE_EXTENSION Extension
)
/*++
Routine Description:
This routine is used to start off any write. It initializes
the Iostatus fields of the irp. It will set up any timers
that are used to control the write.
Arguments:
Extension - Points to the serial device extension
Return Value:
This routine will return STATUS_PENDING for all writes
other than those that we find are cancelled.
--*/
{
PIRP NewIrp;
KIRQL OldIrql;
LARGE_INTEGER TotalTime;
BOOLEAN UseATimer;
SERIAL_TIMEOUTS Timeouts;
BOOLEAN SetFirstStatus = FALSE;
NTSTATUS FirstStatus;
//SERIAL_LOCKED_PAGED_CODE();
//SerialDump(SERTRACECALLS, ("SERIAL: SerialStartWrite\n"));
do {
//
// If there is an xoff counter then complete it.
//
IoAcquireCancelSpinLock(&OldIrql);
//
// We see if there is a actually an Xoff counter irp.
//
// If there is, we put the write irp back on the head
// of the write list. We then kill the xoff counter.
// The xoff counter killing code will actually make the
// xoff counter back into the current write irp, and
// in the course of completing the xoff (which is now
// the current write) we will restart this irp.
//
if (Extension->CurrentXoffIrp) {
if (SERIAL_REFERENCE_COUNT(Extension->CurrentXoffIrp)) {
//
// The reference count is non-zero. This implies that
// the xoff irp has not made it through the completion
// path yet. We will increment the reference count
// and attempt to complete it ourseleves.
//
SERIAL_SET_REFERENCE(
Extension->CurrentXoffIrp,
SERIAL_REF_XOFF_REF
);
Extension->CurrentXoffIrp->IoStatus.Information = 0;
//
// The following call will actually release the
// cancel spin lock.
//
SerialTryToCompleteCurrent(
Extension,
SerialGrabXoffFromIsr,
OldIrql,
STATUS_SERIAL_MORE_WRITES,
&Extension->CurrentXoffIrp,
NULL,
NULL,
&Extension->XoffCountTimer,
NULL,
NULL,
SERIAL_REF_XOFF_REF
);
} else {
//
// The irp is well on its way to being finished.
// We can let the regular completion code do the
// work. Just release the spin lock.
//
IoReleaseCancelSpinLock(OldIrql);
}
} else {
IoReleaseCancelSpinLock(OldIrql);
}
UseATimer = FALSE;
//
// Calculate the timeout value needed for the
// request. Note that the values stored in the
// timeout record are in milliseconds. Note that
// if the timeout values are zero then we won't start
// the timer.
//
KeAcquireSpinLock(
&Extension->ControlLock,
&OldIrql
);
Timeouts = Extension->Timeouts;
KeReleaseSpinLock(
&Extension->ControlLock,
OldIrql
);
if (Timeouts.WriteTotalTimeoutConstant ||
Timeouts.WriteTotalTimeoutMultiplier) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(
Extension->CurrentWriteIrp
);
UseATimer = TRUE;
//
// We have some timer values to calculate.
//
// Take care, we might have an xoff counter masquerading
// as a write.
//
TotalTime.QuadPart =
((LONGLONG)((UInt32x32To64(
(IrpSp->MajorFunction == IRP_MJ_WRITE)?
(IrpSp->Parameters.Write.Length):
(1),
Timeouts.WriteTotalTimeoutMultiplier
)
+ Timeouts.WriteTotalTimeoutConstant)))
* -10000;
}
//
// The irp may be going to the isr shortly. Now
// is a good time to initialize its reference counts.
//
SERIAL_INIT_REFERENCE(Extension->CurrentWriteIrp);
//
// We need to see if this irp should be canceled.
//
IoAcquireCancelSpinLock(&OldIrql);
if (Extension->CurrentWriteIrp->Cancel) {
IoReleaseCancelSpinLock(OldIrql);
Extension->CurrentWriteIrp->IoStatus.Status = STATUS_CANCELLED;
if (!SetFirstStatus) {
FirstStatus = STATUS_CANCELLED;
SetFirstStatus = TRUE;
}
} else {
if (!SetFirstStatus) {
//
// If we haven't set our first status, then
// this is the only irp that could have possibly
// not been on the queue. (It could have been
// on the queue if this routine is being invoked
// from the completion routine.) Since this
// irp might never have been on the queue we
// should mark it as pending.
//
IoMarkIrpPending(Extension->CurrentWriteIrp);
SetFirstStatus = TRUE;
FirstStatus = STATUS_PENDING;
}
//
// We give the irp to the isr to write out.
// We set a cancel routine that knows how to
// grab the current write away from the isr.
//
// Since the cancel routine has an implicit reference
// to this irp up the reference count.
//
IoSetCancelRoutine(
Extension->CurrentWriteIrp,
SerialCancelCurrentWrite
);
SERIAL_SET_REFERENCE(
Extension->CurrentWriteIrp,
SERIAL_REF_CANCEL
);
if (UseATimer) {
SerialSetTimer(
&Extension->WriteRequestTotalTimer,
TotalTime,
&Extension->TotalWriteTimeoutDpc,
Extension
);
//
// This timer now has a reference to the irp.
//
SERIAL_SET_REFERENCE(
Extension->CurrentWriteIrp,
SERIAL_REF_TOTAL_TIMER
);
}
/*
KeSynchronizeExecution(
Extension->Interrupt,
SerialGiveWriteToIsr,
Extension
);
*/
SerialGiveWriteToIsr(Extension); //henry
IoReleaseCancelSpinLock(OldIrql);
break;
}
//
// Well the write was canceled before we could start it up.
// Try to get another.
//
SerialGetNextWrite(
&Extension->CurrentWriteIrp,
&Extension->WriteQueue,
&NewIrp,
TRUE,
Extension
);
} while (NewIrp);
return FirstStatus;
}
VOID
SerialGetNextWrite(
IN PIRP *CurrentOpIrp,
IN PLIST_ENTRY QueueToProcess,
IN PIRP *NewIrp,
IN BOOLEAN CompleteCurrent,
PDEVICE_EXTENSION Extension
)
/*++
Routine Description:
This routine completes the old write as well as getting
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -