/*++ Routine Description: This routine is used to grab an xoff counter request from the isr when it is no longer masquerading as a write request. This routine is called by the cancel and timeout code for the xoff counter ioctl. NOTE: This routine is being called from WdfInterruptSynchronize. 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. --*/ _Use_decl_annotations_ BOOLEAN SerialGrabXoffFromIsr( WDFINTERRUPT Interrupt, PVOID Context ) { PSERIAL_DEVICE_EXTENSION extension = Context; PREQUEST_CONTEXT reqContext; UNREFERENCED_PARAMETER(Interrupt); reqContext = SerialGetRequestContext(extension->CurrentXoffRequest); 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 request. SERIAL_CLEAR_REFERENCE(reqContext, SERIAL_REF_ISR); } return FALSE; }
BOOLEAN SerialGrabImmediateFromIsr( IN WDFINTERRUPT Interrupt, IN PVOID Context ) /*++ Routine Description: This routine is used to grab the current request, which could be timing out or canceling, from the ISR NOTE: This routine is being called from WdfInterruptSynchronize. 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. --*/ { PSERIAL_DEVICE_EXTENSION Extension = Context; PREQUEST_CONTEXT reqContext; UNREFERENCED_PARAMETER(Interrupt); reqContext = SerialGetRequestContext(Extension->CurrentImmediateRequest); if (Extension->TransmitImmediate) { Extension->TransmitImmediate = FALSE; // // Since the isr no longer references this request, we can // decrement it's reference count. // SERIAL_CLEAR_REFERENCE( reqContext, SERIAL_REF_ISR ); } return FALSE; }
/*++ Routine Description: This routine is used to grab the current request, which could be timing out or canceling, from the ISR NOTE: This routine is being called from WdfInterruptSynchronize. 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. --*/ _Use_decl_annotations_ BOOLEAN SerialGrabWriteFromIsr( WDFINTERRUPT Interrupt, PVOID Context ) { PSERIAL_DEVICE_EXTENSION extension = Context; PREQUEST_CONTEXT reqContext; UNREFERENCED_PARAMETER(Interrupt); reqContext = SerialGetRequestContext(extension->CurrentWriteRequest); // Check if the write length is non-zero. If it is non-zero // then the ISR still owns the request. We calculate the the number // of characters written and update the information field of the // request with the characters written. We then clear the write length // the isr sees. if (extension->WriteLength) { // We could have an xoff counter masquerading as a // write request. If so, don't update the write length. if (reqContext->MajorFunction == IRP_MJ_WRITE) { reqContext->Information = reqContext->Length -extension->WriteLength; } else { reqContext->Information = 0; } // Since the isr no longer references this request, we can // decrement it's reference count. SERIAL_CLEAR_REFERENCE(reqContext, SERIAL_REF_ISR); extension->WriteLength = 0; } return FALSE; }
VOID SerialRundownIrpRefs( IN WDFREQUEST *CurrentOpRequest, IN WDFTIMER IntervalTimer OPTIONAL, IN WDFTIMER TotalTimer OPTIONAL, IN PSERIAL_DEVICE_EXTENSION PDevExt, IN LONG RefType ) /*++ Routine Description: This routine runs through the various items that *could* have a reference to the current read/write. It try's to remove the reason. If it does succeed in removing the reason it will decrement the reference count on the request. NOTE: This routine assumes that it is called with the cancel spin lock held. Arguments: CurrentOpRequest - Pointer to a pointer to current request for the particular operation. IntervalTimer - Pointer to the interval timer for the operation. NOTE: This could be null. TotalTimer - Pointer to the total timer for the operation. NOTE: This could be null. PDevExt - Pointer to device extension Return Value: None. --*/ { PREQUEST_CONTEXT reqContext; WDFREQUEST request = *CurrentOpRequest; reqContext = SerialGetRequestContext(request); if(RefType == SERIAL_REF_CANCEL) { // // Caller is a cancel routine. So just clear the reference. // SERIAL_CLEAR_REFERENCE( reqContext, SERIAL_REF_CANCEL ); reqContext->CancelRoutine = NULL; } else { // // Try to clear the cancelable state. // SerialClearCancelRoutine(request, TRUE); } if (IntervalTimer) { // // Try to cancel the operations interval timer. If the operation // returns true then the timer did have a reference to the // request. Since we've canceled this timer that reference is // no longer valid and we can decrement the reference count. // // If the cancel returns false then this means either of two things: // // a) The timer has already fired. // // b) There never was an interval timer. // // In the case of "b" there is no need to decrement the reference // count since the "timer" never had a reference to it. // // In the case of "a", then the timer itself will be coming // along and decrement it's reference. Note that the caller // of this routine might actually be the this timer, so // decrement the reference. // if (SerialCancelTimer(IntervalTimer, PDevExt)) { SERIAL_CLEAR_REFERENCE( reqContext, SERIAL_REF_INT_TIMER ); } else if(RefType == SERIAL_REF_INT_TIMER) { // caller is the timer SERIAL_CLEAR_REFERENCE( reqContext, SERIAL_REF_INT_TIMER ); } } if (TotalTimer) { // // Try to cancel the operations total timer. If the operation // returns true then the timer did have a reference to the // request. Since we've canceled this timer that reference is // no longer valid and we can decrement the reference count. // // If the cancel returns false then this means either of two things: // // a) The timer has already fired. // // b) There never was an total timer. // // In the case of "b" there is no need to decrement the reference // count since the "timer" never had a reference to it. // // In the case of "a", then the timer itself will be coming // along and decrement it's reference. Note that the caller // of this routine might actually be the this timer, so // decrement the reference. // if (SerialCancelTimer(TotalTimer, PDevExt)) { SERIAL_CLEAR_REFERENCE( reqContext, SERIAL_REF_TOTAL_TIMER ); } else if(RefType == SERIAL_REF_TOTAL_TIMER) { // caller is the timer SERIAL_CLEAR_REFERENCE( reqContext, SERIAL_REF_TOTAL_TIMER ); } } }
VOID SerialTryToCompleteCurrent( IN PSERIAL_DEVICE_EXTENSION Extension, IN PFN_WDF_INTERRUPT_SYNCHRONIZE SynchRoutine OPTIONAL, IN NTSTATUS StatusToUse, IN WDFREQUEST *CurrentOpRequest, IN WDFQUEUE QueueToProcess OPTIONAL, IN WDFTIMER IntervalTimer OPTIONAL, IN WDFTIMER TotalTimer OPTIONAL, IN PSERIAL_START_ROUTINE Starter OPTIONAL, IN PSERIAL_GET_NEXT_ROUTINE GetNextRequest OPTIONAL, IN LONG RefType ) /*++ Routine Description: This routine attempts to remove all of the reasons there are references on the current read/write. If everything can be completed it will complete this read/write and try to start another. NOTE: This routine assumes that it is called with the cancel spinlock held. Arguments: Extension - Simply a pointer to the device extension. SynchRoutine - A routine that will synchronize with the isr and attempt to remove the knowledge of the current request from the isr. NOTE: This pointer can be null. IrqlForRelease - This routine is called with the cancel spinlock held. This is the irql that was current when the cancel spinlock was acquired. StatusToUse - The request's status field will be set to this value, if this routine can complete the request. Return Value: None. --*/ { PREQUEST_CONTEXT reqContext; ASSERTMSG("SerialTryToCompleteCurrent: CurrentOpRequest is NULL", *CurrentOpRequest); reqContext = SerialGetRequestContext(*CurrentOpRequest); if(RefType == SERIAL_REF_ISR || RefType == SERIAL_REF_XOFF_REF) { // // We can decrement the reference to "remove" the fact // that the caller no longer will be accessing this request. // SERIAL_CLEAR_REFERENCE( reqContext, RefType ); } if (SynchRoutine) { WdfInterruptSynchronize( Extension->WdfInterrupt, SynchRoutine, Extension ); } // // Try to run down all other references to this request. // SerialRundownIrpRefs( CurrentOpRequest, IntervalTimer, TotalTimer, Extension, RefType ); if(StatusToUse == STATUS_CANCELLED) { // // This function is called from a cancelroutine. So mark // the request as cancelled. We need to do this because // we may not complete the request below if somebody // else has a reference to it. // This state variable was added to avoid calling // WdfRequestMarkCancelable second time on a request that // has cancelled but wasn't completed in the cancel routine. // reqContext->Cancelled = TRUE; } // // See if the ref count is zero after trying to complete everybody else. // if (!SERIAL_REFERENCE_COUNT(reqContext)) { WDFREQUEST newRequest; // // The ref count was zero so we should complete this // request. // // The following call will also cause the current request to be // completed. // reqContext->Status = StatusToUse; if (StatusToUse == STATUS_CANCELLED) { reqContext->Information = 0; } if (GetNextRequest) { GetNextRequest( CurrentOpRequest, QueueToProcess, &newRequest, TRUE, Extension ); if (newRequest) { Starter(Extension); } } else { WDFREQUEST oldRequest = *CurrentOpRequest; // // There was no get next routine. We will simply complete // the request. We should make sure that we null out the // pointer to the pointer to this request. // *CurrentOpRequest = NULL; SerialCompleteRequest(oldRequest, reqContext->Status, reqContext->Information); } } else { } }