/*++ 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); }