/*-------------------------------------------------------------------------- * * Copyright (C) Cyclades Corporation, 1997-2001. * All rights reserved. * * Cyclades-Z Port Driver * * This file: cyzwrite.c * * Description: This module contains the code related to write * operations in the Cyclades-Z Port driver. * * Notes: This code supports Windows 2000 and Windows XP, * x86 and IA64 processors. * * Complies with Cyclades SW Coding Standard rev 1.3. * *-------------------------------------------------------------------------- */ /*------------------------------------------------------------------------- * * Change History * *-------------------------------------------------------------------------- * * *-------------------------------------------------------------------------- */ #include "precomp.h" BOOLEAN CyzGiveWriteToIsr( IN PVOID Context ); VOID CyzCancelCurrentWrite( PDEVICE_OBJECT DeviceObject, PIRP Irp ); BOOLEAN CyzGrabWriteFromIsr( IN PVOID Context ); BOOLEAN CyzGrabXoffFromIsr( IN PVOID Context ); VOID CyzCancelCurrentXoff( PDEVICE_OBJECT DeviceObject, PIRP Irp ); BOOLEAN CyzGiveXoffToIsr( IN PVOID Context ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGESER,CyzProcessEmptyTransmit) #pragma alloc_text(PAGESER,CyzWrite) #pragma alloc_text(PAGESER,CyzStartWrite) #pragma alloc_text(PAGESER,CyzGetNextWrite) #pragma alloc_text(PAGESER,CyzGiveWriteToIsr) #pragma alloc_text(PAGESER,CyzCancelCurrentWrite) #pragma alloc_text(PAGESER,CyzGrabWriteFromIsr) #pragma alloc_text(PAGESER,CyzGrabXoffFromIsr) #pragma alloc_text(PAGESER,CyzCancelCurrentXoff) #pragma alloc_text(PAGESER,CyzGiveXoffToIsr) #endif NTSTATUS CyzWrite( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*-------------------------------------------------------------------------- CyzWrite() 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. --------------------------------------------------------------------------*/ { PCYZ_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension; NTSTATUS status; CYZ_LOCKED_PAGED_CODE(); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzWrite(%X, %X)\n", DeviceObject, Irp); LOGENTRY(LOG_MISC, ZSIG_WRITE, Extension->PortIndex+1, IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length, 0); if ((status = CyzIRPPrologue(Irp, Extension)) != STATUS_SUCCESS) { if (status != STATUS_PENDING) { CyzCompleteRequest(Extension, Irp, IO_NO_INCREMENT); } CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "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. // status = CyzStartOrQueue(Extension, Irp, &Extension->WriteQueue, &Extension->CurrentWriteIrp, CyzStartWrite); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "IoStatus.Status = STATUS_SUCCESS; CyzCompleteRequest(Extension, Irp, 0); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "CyzStartWrite(%X)\n", Extension); // LOGENTRY(LOG_MISC, ZSIG_START_WRITE, // Extension->PortIndex+1, // 0, // 0); 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; // Added in build 2128 // The following call will actually release the // cancel spin lock. CyzTryToCompleteCurrent( Extension, CyzGrabXoffFromIsr, 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 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, CyzCancelCurrentWrite ); SERIAL_SET_REFERENCE(Extension->CurrentWriteIrp,SERIAL_REF_CANCEL); if (UseATimer) { CyzSetTimer( &Extension->WriteRequestTotalTimer, TotalTime, &Extension->TotalWriteTimeoutDpc, Extension ); // This timer now has a reference to the irp. SERIAL_SET_REFERENCE( Extension->CurrentWriteIrp, SERIAL_REF_TOTAL_TIMER ); } #ifdef POLL KeAcquireSpinLock(&Extension->PollLock,&pollIrql); CyzGiveWriteToIsr(Extension); KeReleaseSpinLock(&Extension->PollLock,pollIrql); #else KeSynchronizeExecution( Extension->Interrupt, CyzGiveWriteToIsr, Extension ); #endif IoReleaseCancelSpinLock(OldIrql); break; } // Well the write was canceled before we could start it up. // Try to get another. CyzGetNextWrite(&Extension->CurrentWriteIrp, &Extension->WriteQueue, &NewIrp, TRUE, Extension); } while (NewIrp); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "CyzGetNextWrite(XXX)\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; #ifdef POLL KIRQL pollIrql; #endif 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, CyzCancelCurrentXoff ); 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; #ifdef POLL KeAcquireSpinLock(&Extension->PollLock,&pollIrql); CyzGiveXoffToIsr(Extension); KeReleaseSpinLock(&Extension->PollLock,pollIrql); #else KeSynchronizeExecution( Extension->Interrupt, CyzGiveXoffToIsr, Extension ); #endif // // 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 )); CyzSetTimer( &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. // CyzGetNextIrp( CurrentOpIrp, QueueToProcess, NewIrp, CompleteCurrent, Extension ); if (!*NewIrp) { KIRQL OldIrql; #ifdef POLL KIRQL pollIrql; #endif IoAcquireCancelSpinLock(&OldIrql); #ifdef POLL KeAcquireSpinLock(&Extension->PollLock,&pollIrql); CyzProcessEmptyTransmit(Extension); KeReleaseSpinLock(&Extension->PollLock,pollIrql); #else KeSynchronizeExecution( Extension->Interrupt, CyzProcessEmptyTransmit, Extension ); #endif 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); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "CyzCompleteWrite(%X)\n", Extension); // LOGENTRY(LOG_MISC, ZSIG_WRITE_COMPLETE, // Extension->PortIndex+1, // 0, // 0); IoAcquireCancelSpinLock(&OldIrql); CyzTryToCompleteCurrent(Extension, NULL, OldIrql, STATUS_SUCCESS, &Extension->CurrentWriteIrp, &Extension->WriteQueue, NULL, &Extension->WriteRequestTotalTimer, CyzStartWrite, CyzGetNextWrite, SERIAL_REF_ISR); CyzDpcEpilogue(Extension, Dpc); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "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); CyzInsertQueueDpc( &Extension->CommWaitDpc, NULL, NULL, Extension ); } Extension->CountOfTryingToLowerRTS++; CyzPerhapsLowerRTS(Extension); } return FALSE; } BOOLEAN CyzGiveWriteToIsr( 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. --*/ { PCYZ_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; CYZ_LOCKED_PAGED_CODE(); // LOGENTRY(LOG_MISC, ZSIG_GIVE_WRITE_TO_ISR, // Extension->PortIndex+1, // 0, // 0); 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. // //Removed at 02/07/00 by Fanny. Polling routine will do the transmission. 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 DBG //{ // PUCHAR writeptr = Extension->WriteCurrentChar; // int ivar; // for (ivar=0; ivar< Extension->WriteLength; ivar++) { // //DbgPrint("%c",writeptr[ivar]); // LOGENTRY(LOG_MISC,ZSIG_TRANSMIT,0,0,writeptr[ivar]); // } // //DbgPrint("\n"); //} //#endif //if (Extension->HoldingEmpty) { // enable transmit intr CyzTxStart(Extension); //} } // // 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) { CyzSetRTS(Extension); } return FALSE; } VOID CyzCancelCurrentWrite( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: This routine is used to cancel the current write. Arguments: DeviceObject - Pointer to the device object for this device Irp - Pointer to the IRP to be canceled. Return Value: None. --*/ { PCYZ_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension; CYZ_LOCKED_PAGED_CODE(); CyzTryToCompleteCurrent( Extension, CyzGrabWriteFromIsr, Irp->CancelIrql, STATUS_CANCELLED, &Extension->CurrentWriteIrp, &Extension->WriteQueue, NULL, &Extension->WriteRequestTotalTimer, CyzStartWrite, CyzGetNextWrite, SERIAL_REF_CANCEL ); } VOID CyzWriteTimeout( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemContext1, IN PVOID SystemContext2 ) /*++ Routine Description: This routine will try to timeout the current write. Arguments: Dpc - Not Used. DeferredContext - Really points to the device extension. SystemContext1 - Not Used. SystemContext2 - Not Used. Return Value: None. --*/ { PCYZ_DEVICE_EXTENSION Extension = DeferredContext; KIRQL OldIrql; UNREFERENCED_PARAMETER(SystemContext1); UNREFERENCED_PARAMETER(SystemContext2); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzWriteTimeout(%X)\n", Extension); IoAcquireCancelSpinLock(&OldIrql); CyzTryToCompleteCurrent(Extension, CyzGrabWriteFromIsr, OldIrql, STATUS_TIMEOUT, &Extension->CurrentWriteIrp, &Extension->WriteQueue, NULL, &Extension->WriteRequestTotalTimer, CyzStartWrite, CyzGetNextWrite, SERIAL_REF_TOTAL_TIMER); CyzDpcEpilogue(Extension, Dpc); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "WriteLength) { // // We could have an xoff counter masquerading as a // write irp. If so, don't update the write length. // if (IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp) ->MajorFunction == IRP_MJ_WRITE) { Extension->CurrentWriteIrp->IoStatus.Information = IoGetCurrentIrpStackLocation( Extension->CurrentWriteIrp )->Parameters.Write.Length - Extension->WriteLength; } else { Extension->CurrentWriteIrp->IoStatus.Information = 0; } // // Since the isr no longer references this irp, we can // decrement it's reference count. // SERIAL_CLEAR_REFERENCE( Extension->CurrentWriteIrp, SERIAL_REF_ISR ); Extension->WriteLength = 0; if (Extension->ReturnStatusAfterFwEmpty) { struct BUF_CTRL *buf_ctrl; ULONG tx_get, tx_put; Extension->ReturnWriteStatus = FALSE; // Flush fw buffer and Startech FIFO. CyzIssueCmd(Extension,C_CM_FLUSH_TX,0L,TRUE); // Flush transmission buffer in the firmware //buf_ctrl = Extension->BufCtrl; //tx_put = CYZ_READ_ULONG(&buf_ctrl->tx_put); //tx_get = CYZ_READ_ULONG(&buf_ctrl->tx_get); //while (tx_put != tx_get) { // tx_put = tx_get; // CYZ_WRITE_ULONG(&buf_ctrl->tx_put,tx_put); // tx_get = CYZ_READ_ULONG(&buf_ctrl->tx_get); //} } } return FALSE; } BOOLEAN CyzGrabXoffFromIsr( IN PVOID Context ) /*++ Routine Description: This routine is used to grab an xoff counter irp from the isr when it is no longer masquerading as a write irp. This routine is called by the cancel and timeout code for the xoff counter ioctl. NOTE: This routine is being called from KeSynchronizeExecution. NOTE: This routine assumes that the cancel spin lock is held when this routine is called. Arguments: Context - Really a pointer to the device extension. Return Value: Always false. --*/ { PCYZ_DEVICE_EXTENSION Extension = Context; CYZ_LOCKED_PAGED_CODE(); if (Extension->CountSinceXoff) { // // This is only non-zero when there actually is a Xoff ioctl // counting down. // Extension->CountSinceXoff = 0; // // We decrement the count since the isr no longer owns // the irp. // SERIAL_CLEAR_REFERENCE( Extension->CurrentXoffIrp, SERIAL_REF_ISR ); } return FALSE; } VOID CyzCompleteXoff( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemContext1, IN PVOID SystemContext2 ) /*++ Routine Description: This routine is merely used to truely complete an xoff counter irp. 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. --*/ { PCYZ_DEVICE_EXTENSION Extension = DeferredContext; KIRQL OldIrql; UNREFERENCED_PARAMETER(SystemContext1); UNREFERENCED_PARAMETER(SystemContext2); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzCompleteXoff(%X)\n", Extension); IoAcquireCancelSpinLock(&OldIrql); CyzTryToCompleteCurrent(Extension, NULL, OldIrql, STATUS_SUCCESS, &Extension->CurrentXoffIrp, NULL, NULL, &Extension->XoffCountTimer, NULL, NULL, SERIAL_REF_ISR); CyzDpcEpilogue(Extension, Dpc); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "CyzTimeoutXoff(%X)\n", Extension); IoAcquireCancelSpinLock(&OldIrql); CyzTryToCompleteCurrent(Extension, CyzGrabXoffFromIsr, OldIrql, STATUS_SERIAL_COUNTER_TIMEOUT, &Extension->CurrentXoffIrp, NULL, NULL, NULL, NULL, NULL, SERIAL_REF_TOTAL_TIMER); CyzDpcEpilogue(Extension, Dpc); CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "DeviceExtension; CYZ_LOCKED_PAGED_CODE(); CyzTryToCompleteCurrent( Extension, CyzGrabXoffFromIsr, Irp->CancelIrql, STATUS_CANCELLED, &Extension->CurrentXoffIrp, NULL, NULL, &Extension->XoffCountTimer, NULL, NULL, SERIAL_REF_CANCEL ); } BOOLEAN CyzGiveXoffToIsr( IN PVOID Context ) /*++ Routine Description: This routine starts off the xoff counter. It merely has to set the xoff count and increment the reference count to denote that the isr has a reference to the irp. 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. --*/ { PCYZ_DEVICE_EXTENSION Extension = Context; // // The current stack location. This contains all of the // information we need to process this particular request. // PSERIAL_XOFF_COUNTER Xc = Extension->CurrentXoffIrp->AssociatedIrp.SystemBuffer; CYZ_LOCKED_PAGED_CODE(); ASSERT(Extension->CurrentXoffIrp); Extension->CountSinceXoff = Xc->Counter; // // The isr now has a reference to the irp. // SERIAL_SET_REFERENCE( Extension->CurrentXoffIrp, SERIAL_REF_ISR ); return FALSE; }