Leaked source code of windows server 2003
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.
 
 
 
 
 
 

887 lines
21 KiB

/*++
Module Name:
write.c
Environment:
Kernel mode
Revision History :
--*/
#include "precomp.h"
NTSTATUS
MoxaWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
NTSTATUS status;
PMOXA_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
// MoxaKdPrint(MX_DBG_TRACE,("Enter MoxaWrite\n"));
if ((extension->ControlDevice == TRUE)||
(extension->DeviceIsOpened == FALSE) ||
(extension->PowerState != PowerDeviceD0) ) {
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information=0L;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_CANCELLED;
}
if ((status = MoxaIRPPrologue(Irp, extension)) != STATUS_SUCCESS) {
MoxaCompleteRequest(extension, Irp, IO_NO_INCREMENT);
return status;
}
if (MoxaCompleteIfError(
DeviceObject,
Irp
) != STATUS_SUCCESS) {
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.
//
return MoxaStartOrQueue(
extension,
Irp,
&extension->WriteQueue,
&extension->CurrentWriteIrp,
MoxaStartWrite
);
}
else {
Irp->IoStatus.Status = STATUS_SUCCESS;
MoxaCompleteRequest(
extension,
Irp,
0
);
return STATUS_SUCCESS;
}
}
NTSTATUS
MoxaStartWrite(
IN PMOXA_DEVICE_EXTENSION Extension
)
{
PIRP newIrp;
KIRQL oldIrql;
KIRQL controlIrql;
LARGE_INTEGER totalTime;
PIO_STACK_LOCATION irpSp;
BOOLEAN useATimer;
SERIAL_TIMEOUTS timeouts;
BOOLEAN setFirstStatus = FALSE;
NTSTATUS firstStatus;
do {
irpSp = IoGetCurrentIrpStackLocation(
Extension->CurrentWriteIrp
);
//
// Check if MOXA_IOCTL_PutB request
//
if (irpSp->MajorFunction != IRP_MJ_WRITE) {
KeAcquireSpinLock(
&Extension->ControlLock,
&controlIrql
);
IoAcquireCancelSpinLock(&oldIrql);
if (Extension->CurrentWriteIrp->Cancel) {
Extension->CurrentWriteIrp->IoStatus.Status = STATUS_CANCELLED;
IoReleaseCancelSpinLock(oldIrql);
KeReleaseSpinLock(
&Extension->ControlLock,
controlIrql
);
if (!setFirstStatus) {
firstStatus = STATUS_CANCELLED;
setFirstStatus = TRUE;
}
}
else {
KeSynchronizeExecution(
Extension->Interrupt,
MoxaPutB,
Extension
);
if (!setFirstStatus) {
setFirstStatus = TRUE;
firstStatus = STATUS_SUCCESS;
}
Extension->CurrentWriteIrp->IoStatus.Status = STATUS_SUCCESS;
Extension->CurrentWriteIrp->IoStatus.Information = sizeof(ULONG);
IoReleaseCancelSpinLock(oldIrql);
KeReleaseSpinLock(
&Extension->ControlLock,
controlIrql
);
}
}
else {
/*
* Extension->TotalCharsQueued NOT include current write
*
*/
IoAcquireCancelSpinLock(&oldIrql);
Extension->TotalCharsQueued -=
IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp)
->Parameters.Write.Length;
IoReleaseCancelSpinLock(oldIrql);
useATimer = FALSE;
KeAcquireSpinLock(
&Extension->ControlLock,
&controlIrql
);
timeouts = Extension->Timeouts;
KeReleaseSpinLock(
&Extension->ControlLock,
controlIrql
);
if (timeouts.WriteTotalTimeoutConstant ||
timeouts.WriteTotalTimeoutMultiplier) {
useATimer = TRUE;
totalTime = RtlEnlargedUnsignedMultiply(
irpSp->Parameters.Write.Length,
timeouts.WriteTotalTimeoutMultiplier
);
totalTime = RtlLargeIntegerAdd(
totalTime,
RtlConvertUlongToLargeInteger(
timeouts.WriteTotalTimeoutConstant
)
);
totalTime = RtlExtendedIntegerMultiply(
totalTime,
-10000
);
}
KeAcquireSpinLock(
&Extension->ControlLock,
&controlIrql
);
MOXA_INIT_REFERENCE(Extension->CurrentWriteIrp);
IoAcquireCancelSpinLock(&oldIrql);
if (Extension->CurrentWriteIrp->Cancel) {
Extension->CurrentWriteIrp->IoStatus.Status = STATUS_CANCELLED;
IoReleaseCancelSpinLock(oldIrql);
KeReleaseSpinLock(
&Extension->ControlLock,
controlIrql
);
if (!setFirstStatus) {
firstStatus = STATUS_CANCELLED;
setFirstStatus = TRUE;
}
}
else {
KeSynchronizeExecution(
Extension->Interrupt,
MoxaOut,
Extension
);
if (WRcompFlag) { /* complete write */
if (!setFirstStatus) {
setFirstStatus = TRUE;
firstStatus = STATUS_SUCCESS;
}
Extension->CurrentWriteIrp->IoStatus.Status = STATUS_SUCCESS;
Extension->CurrentWriteIrp->IoStatus.Information =
irpSp->Parameters.Write.Length;
IoReleaseCancelSpinLock(oldIrql);
KeReleaseSpinLock(
&Extension->ControlLock,
controlIrql
);
}
else {
if (!setFirstStatus) {
IoMarkIrpPending(Extension->CurrentWriteIrp);
setFirstStatus = TRUE;
firstStatus = STATUS_PENDING;
}
IoSetCancelRoutine(
Extension->CurrentWriteIrp,
MoxaCancelCurrentWrite
);
MOXA_INC_REFERENCE(Extension->CurrentWriteIrp);
if (useATimer) {
MoxaSetTimer(
&Extension->WriteRequestTotalTimer,
totalTime,
&Extension->TotalWriteTimeoutDpc,
Extension
);
MOXA_INC_REFERENCE(Extension->CurrentWriteIrp);
}
IoReleaseCancelSpinLock(oldIrql);
KeReleaseSpinLock(
&Extension->ControlLock,
controlIrql
);
break;
}
}
}
MoxaGetNextWrite(
&Extension->CurrentWriteIrp,
&Extension->WriteQueue,
&newIrp,
TRUE,
Extension
);
} while (newIrp);
return firstStatus;
}
BOOLEAN
MoxaPutB(
IN PVOID Context
)
{
PMOXA_DEVICE_EXTENSION extension = Context;
PMOXA_IOCTL_PUTB Pb;
/*
PUCHAR base, ofs, buff, writeChar;
PUSHORT rptr, wptr;
USHORT txMask, spage, epage, bufHead;
USHORT tail, head, count, count2;
USHORT cnt, pageNo, pageOfs;
ULONG dataLen;
*/
Pb = (PMOXA_IOCTL_PUTB)extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer;
PBdataLen = Pb->DataLen;
PBwriteChar = Pb->DataBuffer;
PBbase = extension->PortBase;
PBofs = extension->PortOfs;
PBbuff = PBbase + DynPage_addr;
PBrptr = (PUSHORT)(PBofs + TXrptr);
PBwptr = (PUSHORT)(PBofs + TXwptr);
PBtxMask = *(PUSHORT)(PBofs + TX_mask);
PBspage = *(PUSHORT)(PBofs + Page_txb);
PBepage = *(PUSHORT)(PBofs + EndPage_txb);
PBtail = *PBwptr;
PBhead = *PBrptr;
PBcount = (PBhead > PBtail) ? (PBhead - PBtail - 1)
: (PBhead - PBtail + PBtxMask);
if (PBcount < PBdataLen) { /* Tx buffer no enough space! */
*(PULONG)extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer = 0;
return FALSE;
}
if (PBspage == PBepage) {
PBbufHead = *(PUSHORT)(PBofs + Ofs_txb);
PBcount = (USHORT)PBdataLen;
*(PBbase + Control_reg) = (UCHAR)PBspage;
if (PBtail & 1) {
PBbuff[PBbufHead+PBtail++] = *PBwriteChar++;
PBtail &= PBtxMask;
PBcount--;
}
PBcount2 = PBcount >> 1;
while (PBcount2--) {
*(PUSHORT)&(PBbuff[PBbufHead+PBtail]) = *((PUSHORT)PBwriteChar)++;
PBtail += 2;
PBtail &= PBtxMask;
}
if (PBcount & 1) {
PBbuff[PBbufHead+PBtail++] = *PBwriteChar++;
PBtail &= PBtxMask;
}
*PBwptr = PBtail;
*(PBofs + CD180TXirq) = 1; /* start to send */
}
else {
PBcount = (USHORT)PBdataLen;
PBpageNo = PBspage + (PBtail >> 13);
PBpageOfs = PBtail & Page_mask;
do {
PBcnt = Page_size - PBpageOfs;
if (PBcnt > PBcount)
PBcnt = PBcount;
PBcount -= PBcnt;
if (PBcnt) {
*(PBbase + Control_reg) = (UCHAR)PBpageNo;
if (PBpageOfs & 1) {
PBbuff[PBpageOfs++] = *PBwriteChar++;
PBcnt--;
}
PBcount2 = PBcnt >> 1;
while (PBcount2--) {
*(PUSHORT)&(PBbuff[PBpageOfs]) = *((PUSHORT)PBwriteChar)++;
PBpageOfs += 2;
}
if (PBcnt & 1)
PBbuff[PBpageOfs++] = *PBwriteChar++;
}
if (PBcount == 0)
break;
if (++PBpageNo == PBepage)
PBpageNo = PBspage;
PBpageOfs = 0;
} while (TRUE);
*PBwptr = (USHORT)((PBtail + PBdataLen) & PBtxMask);
*(PBofs + CD180TXirq) = 1; /* start to send */
}
extension->PerfStats.TransmittedCount += PBdataLen;
return FALSE;
}
BOOLEAN
MoxaOut(
IN PVOID Context
)
{
PMOXA_DEVICE_EXTENSION extension = Context;
PIO_STACK_LOCATION irpSp;
irpSp = IoGetCurrentIrpStackLocation(
extension->CurrentWriteIrp
);
extension->WriteLength = irpSp->Parameters.Write.Length;
extension->WriteCurrentChar =
extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer;
if (MoxaPutData(extension)) {
MOXA_INC_REFERENCE(extension->CurrentWriteIrp);
if (extension->PortFlag & NORMAL_TX_MODE)
*(PUSHORT)(extension->PortOfs + HostStat) |= WakeupTx;
else
*(PUSHORT)(extension->PortOfs + HostStat) |= WakeupTxTrigger;
WRcompFlag = FALSE;
}
else
WRcompFlag = TRUE;
return FALSE;
}
BOOLEAN
MoxaPutData (
IN PMOXA_DEVICE_EXTENSION Extension
)
{
/*
PUCHAR base, ofs, buff, writeChar;
PUSHORT rptr, wptr;
USHORT txMask, spage, epage, bufHead;
USHORT tail, head, count, count2;
USHORT cnt, len, pageNo, pageOfs;
ULONG dataLen;
*/
PDbase = Extension->PortBase;
PDofs = Extension->PortOfs;
PDbuff = PDbase + DynPage_addr;
PDrptr = (PUSHORT)(PDofs + TXrptr);
PDwptr = (PUSHORT)(PDofs + TXwptr);
PDtxMask = *(PUSHORT)(PDofs + TX_mask);
PDspage = *(PUSHORT)(PDofs + Page_txb);
PDepage = *(PUSHORT)(PDofs + EndPage_txb);
PDtail = *PDwptr;
PDhead = *PDrptr;
PDdataLen = Extension->WriteLength;
PDwriteChar = Extension->WriteCurrentChar;
PDcount = (PDhead > PDtail) ? (PDhead - PDtail - 1)
: (PDhead - PDtail + PDtxMask);
if (!PDcount) /* Tx buffer no space! */
return TRUE;
if (PDspage == PDepage) {
PDbufHead = *(PUSHORT)(PDofs + Ofs_txb);
if (PDcount > PDdataLen)
PDcount = (USHORT)PDdataLen;
PDdataLen -= PDcount;
PDlen = PDcount;
*(PDbase + Control_reg) = (UCHAR)PDspage;
if (PDtail & 1) {
PDbuff[PDbufHead+PDtail++] = *PDwriteChar++;
PDtail &= PDtxMask;
PDcount--;
}
PDcount2 = PDcount >> 1;
while (PDcount2--) {
*(PUSHORT)&(PDbuff[PDbufHead+PDtail]) = *((PUSHORT)PDwriteChar)++;
PDtail += 2;
PDtail &= PDtxMask;
}
if (PDcount & 1) {
PDbuff[PDbufHead+PDtail++] = *PDwriteChar++;
PDtail &= PDtxMask;
}
*PDwptr = PDtail;
*(PDofs + CD180TXirq) = 1; /* start to send */
}
else {
if (PDcount > PDdataLen)
PDcount = (USHORT)PDdataLen;
PDdataLen -= PDcount;
PDlen = PDcount;
PDpageNo = PDspage + (PDtail >> 13);
PDpageOfs = PDtail & Page_mask;
do {
PDcnt = Page_size - PDpageOfs;
if (PDcnt > PDcount)
PDcnt = PDcount;
PDcount -= PDcnt;
if (PDcnt) {
*(PDbase + Control_reg) = (UCHAR)PDpageNo;
if (PDpageOfs & 1) {
PDbuff[PDpageOfs++] = *PDwriteChar++;
PDcnt--;
}
PDcount2 = PDcnt >> 1;
while (PDcount2--) {
*(PUSHORT)&(PDbuff[PDpageOfs]) = *((PUSHORT)PDwriteChar)++;
PDpageOfs += 2;
}
if (PDcnt & 1)
PDbuff[PDpageOfs++] = *PDwriteChar++;
}
if (PDcount == 0)
break;
if (++PDpageNo == PDepage)
PDpageNo = PDspage;
PDpageOfs = 0;
} while (TRUE);
*PDwptr = (PDtail + PDlen) & PDtxMask;
*(PDofs + CD180TXirq) = 1;
}
Extension->PerfStats.TransmittedCount += PDlen;
Extension->WriteLength = PDdataLen;
if (PDdataLen) {
Extension->WriteCurrentChar = PDwriteChar;
return TRUE;
}
else if (Extension->PortFlag & NORMAL_TX_MODE) {
return TRUE;
}
else {
PDtail = *PDwptr;
PDhead = *PDrptr;
PDcount = (PDtail >= PDhead) ? (PDtail - PDhead)
: (PDtail - PDhead + PDtxMask + 1);
if (PDcount >= MoxaTxLowWater)
return TRUE;
else
return FALSE;
}
}
VOID
MoxaGetNextWrite(
IN PIRP *CurrentOpIrp,
IN PLIST_ENTRY QueueToProcess,
IN PIRP *NewIrp,
IN BOOLEAN CompleteCurrent,
IN PMOXA_DEVICE_EXTENSION Extension
)
{
PMOXA_DEVICE_EXTENSION extension = CONTAINING_RECORD(
QueueToProcess,
MOXA_DEVICE_EXTENSION,
WriteQueue
);
UNREFERENCED_PARAMETER(Extension);
do {
//
// We could be completing a flush.
//
/*
* extension->TotalCharsQueued NOT include current write
*
if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
== IRP_MJ_WRITE) {
KIRQL oldIrql;
IoAcquireCancelSpinLock(&oldIrql);
extension->TotalCharsQueued -=
IoGetCurrentIrpStackLocation(*CurrentOpIrp)
->Parameters.Write.Length;
IoReleaseCancelSpinLock(oldIrql);
}
*/
MoxaGetNextIrp(
CurrentOpIrp,
QueueToProcess,
NewIrp,
CompleteCurrent,
extension
);
if (!*NewIrp) {
KIRQL oldIrql;
IoAcquireCancelSpinLock(&oldIrql);
KeSynchronizeExecution(
extension->Interrupt,
MoxaProcessEmptyTransmit,
extension
);
IoReleaseCancelSpinLock(oldIrql);
break;
}
else if (IoGetCurrentIrpStackLocation(*NewIrp)->MajorFunction
== IRP_MJ_FLUSH_BUFFERS) {
(*NewIrp)->IoStatus.Status = STATUS_SUCCESS;
}
else {
break;
}
} while (TRUE);
}
VOID
MoxaCancelCurrentWrite(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PMOXA_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
MoxaTryToCompleteCurrent(
extension,
MoxaGrabWriteFromIsr,
Irp->CancelIrql,
STATUS_CANCELLED,
&extension->CurrentWriteIrp,
&extension->WriteQueue,
NULL,
&extension->WriteRequestTotalTimer,
MoxaStartWrite,
MoxaGetNextWrite
);
}
BOOLEAN
MoxaGrabWriteFromIsr(
IN PVOID Context
)
{
PMOXA_DEVICE_EXTENSION extension = Context;
if (*(PUSHORT)(extension->PortOfs + HostStat) & (WakeupTx|WakeupTxTrigger)) {
extension->CurrentWriteIrp->IoStatus.Information =
IoGetCurrentIrpStackLocation(
extension->CurrentWriteIrp
)->Parameters.Write.Length -
extension->WriteLength -
GetDeviceTxQueue(extension);
*(PUSHORT)(extension->PortOfs + HostStat) &= ~(WakeupTx|WakeupTxTrigger);
extension->WriteLength = 0;
MOXA_DEC_REFERENCE(extension->CurrentWriteIrp);
MoxaFuncWithDumbWait(extension->PortOfs, FC_FlushQueue, 1); // flush OQueue
}
return FALSE;
}
BOOLEAN
MoxaProcessEmptyTransmit(
IN PVOID Context
)
{
PMOXA_DEVICE_EXTENSION extension = Context;
if ((extension->IsrWaitMask & SERIAL_EV_TXEMPTY) &&
(!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);
MoxaInsertQueueDpc(
&extension->CommWaitDpc,
NULL,
NULL,
extension
);
}
}
return FALSE;
}
VOID
MoxaCompleteWrite(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
{
PMOXA_DEVICE_EXTENSION extension = DeferredContext;
KIRQL oldIrql;
IoAcquireCancelSpinLock(&oldIrql);
MoxaTryToCompleteCurrent(
extension,
NULL,
oldIrql,
STATUS_SUCCESS,
&extension->CurrentWriteIrp,
&extension->WriteQueue,
NULL,
&extension->WriteRequestTotalTimer,
MoxaStartWrite,
MoxaGetNextWrite
);
MoxaDpcEpilogue(extension, Dpc);
}
VOID
MoxaWriteTimeout(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
{
PMOXA_DEVICE_EXTENSION extension = DeferredContext;
KIRQL oldIrql;
IoAcquireCancelSpinLock(&oldIrql);
MoxaTryToCompleteCurrent(
extension,
MoxaGrabWriteFromIsr,
oldIrql,
STATUS_TIMEOUT,
&extension->CurrentWriteIrp,
&extension->WriteQueue,
NULL,
&extension->WriteRequestTotalTimer,
MoxaStartWrite,
MoxaGetNextWrite
);
MoxaDpcEpilogue(extension, Dpc);
}