VOID SerialGetNextImmediate( IN WDFREQUEST *CurrentOpRequest, IN WDFQUEUE QueueToProcess, IN WDFREQUEST *NewRequest, IN BOOLEAN CompleteCurrent, IN PSERIAL_DEVICE_EXTENSION Extension ) /*++ Routine Description: This routine is used to complete the current immediate request. Even though the current immediate will always be completed and there is no queue associated with it, we use this routine so that we can try to satisfy a wait for transmit queue empty event. Arguments: CurrentOpRequest - Pointer to the pointer that points to the current write request. This should point to CurrentImmediateRequest. QueueToProcess - Always NULL. NewRequest - Always NULL on exit to this routine. CompleteCurrent - Should always be true for this routine. Return Value: None. --*/ { WDFREQUEST oldRequest = *CurrentOpRequest; PREQUEST_CONTEXT reqContext = SerialGetRequestContext(oldRequest); UNREFERENCED_PARAMETER(QueueToProcess); UNREFERENCED_PARAMETER(CompleteCurrent); ASSERT(Extension->TotalCharsQueued >= 1); Extension->TotalCharsQueued--; *CurrentOpRequest = NULL; *NewRequest = NULL; WdfInterruptSynchronize( Extension->WdfInterrupt, SerialProcessEmptyTransmit, Extension ); SerialCompleteRequest(oldRequest, reqContext->Status, reqContext->Information); }
NTSTATUS SerialCompleteIfError( PSERIAL_DEVICE_EXTENSION extension, WDFREQUEST Request ) /*++ Routine Description: If the current request is not an IOCTL_SERIAL_GET_COMMSTATUS request and there is an error and the application requested abort on errors, then cancel the request. Arguments: extension - Pointer to the device context Request - Pointer to the WDFREQUEST to test. Return Value: STATUS_SUCCESS or STATUS_CANCELLED. --*/ { WDF_REQUEST_PARAMETERS params; NTSTATUS status = STATUS_SUCCESS; if ((extension->HandFlow.ControlHandShake & SERIAL_ERROR_ABORT) && extension->ErrorWord) { WDF_REQUEST_PARAMETERS_INIT(¶ms); WdfRequestGetParameters( Request, ¶ms ); // // There is a current error in the driver. No requests should // come through except for the GET_COMMSTATUS. // if ((params.Type != WdfRequestTypeDeviceControl) || (params.Parameters.DeviceIoControl.IoControlCode != IOCTL_SERIAL_GET_COMMSTATUS)) { status = STATUS_CANCELLED; SerialCompleteRequest(Request, status, 0); } } return status; }
VOID SerialEvtCanceledOnQueue( IN WDFQUEUE Queue, IN WDFREQUEST Request ) /*++ Routine Description: Called when the request is cancelled while it's waiting on the queue. This callback is used instead of EvtCleanupCallback on the request because this one will be called with the presentation lock held. Arguments: Queue - Queue in which the request currently waiting Request - Request being cancelled Return Value: None. --*/ { PSERIAL_DEVICE_EXTENSION extension = NULL; PREQUEST_CONTEXT reqContext; UNREFERENCED_PARAMETER(Queue); reqContext = SerialGetRequestContext(Request); extension = reqContext->Extension; // // If this is a write request then take the amount of characters // to write and subtract it from the count of characters to write. // if (reqContext->MajorFunction == IRP_MJ_WRITE) { extension->TotalCharsQueued -= reqContext->Length; } else if (reqContext->MajorFunction == IRP_MJ_DEVICE_CONTROL) { // // If it's an immediate then we need to decrement the // count of chars queued. If it's a resize then we // need to deallocate the pool that we're passing on // to the "resizing" routine. // if (( reqContext->IoctlCode == IOCTL_SERIAL_IMMEDIATE_CHAR) || (reqContext->IoctlCode == IOCTL_SERIAL_XOFF_COUNTER)) { extension->TotalCharsQueued--; } else if (reqContext->IoctlCode == IOCTL_SERIAL_SET_QUEUE_SIZE) { // // We shoved the pointer to the memory into the // the type 3 buffer pointer which we KNOW we // never use. // ASSERT(reqContext->Type3InputBuffer); ExFreePool(reqContext->Type3InputBuffer); reqContext->Type3InputBuffer = NULL; } } SerialCompleteRequest(Request, WdfRequestGetStatus(Request), 0); }
VOID SerialStartOrQueue( IN PSERIAL_DEVICE_EXTENSION Extension, IN WDFREQUEST Request, IN WDFQUEUE QueueToExamine, IN WDFREQUEST *CurrentOpRequest, IN PSERIAL_START_ROUTINE Starter ) /*++ Routine Description: This routine is used to either start or queue any requst that can be queued in the driver. Arguments: Extension - Points to the serial device extension. Request - The request to either queue or start. In either case the request will be marked pending. QueueToExamine - The queue the request will be place on if there is already an operation in progress. CurrentOpRequest - Pointer to a pointer to the request the is current for the queue. The pointer pointed to will be set with to Request if what CurrentOpRequest points to is NULL. Starter - The routine to call if the queue is empty. Return Value: --*/ { NTSTATUS status; PREQUEST_CONTEXT reqContext; WDF_REQUEST_PARAMETERS params; reqContext = SerialGetRequestContext(Request); WDF_REQUEST_PARAMETERS_INIT(¶ms); WdfRequestGetParameters( Request, ¶ms); // // If this is a write request then take the amount of characters // to write and add it to the count of characters to write. // if (params.Type == WdfRequestTypeWrite) { Extension->TotalCharsQueued += reqContext->Length; } else if ((params.Type == WdfRequestTypeDeviceControl) && ((params.Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_IMMEDIATE_CHAR) || (params.Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER))) { reqContext->IoctlCode = params.Parameters.DeviceIoControl.IoControlCode; // We need this in the destroy callback Extension->TotalCharsQueued++; } if (IsQueueEmpty(QueueToExamine) && !(*CurrentOpRequest)) { // // There were no current operation. Mark this one as // current and start it up. // *CurrentOpRequest = Request; Starter(Extension); return; } else { // // We don't know how long the request will be in the // queue. If it gets cancelled while waiting in the queue, we will // be notified by EvtCanceledOnQueue callback so that we can readjust // the lenght or free the buffer. // reqContext->Extension = Extension; // We need this in the destroy callback status = WdfRequestForwardToIoQueue(Request, QueueToExamine); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "WdfRequestForwardToIoQueue failed%X\n", status); ASSERTMSG("WdfRequestForwardToIoQueue failed ", FALSE); SerialCompleteRequest(Request, status, 0); } return; } }
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 { } }
VOID SerialGetNextRequest( IN WDFREQUEST * CurrentOpRequest, IN WDFQUEUE QueueToProcess, OUT WDFREQUEST * NextRequest, IN BOOLEAN CompleteCurrent, IN PSERIAL_DEVICE_EXTENSION Extension ) /*++ Routine Description: This function is used to make the head of the particular queue the current request. It also completes the what was the old current request if desired. Arguments: CurrentOpRequest - Pointer to a pointer to the currently active request for the particular work list. Note that this item is not actually part of the list. QueueToProcess - The list to pull the new item off of. NextIrp - The next Request to process. Note that CurrentOpRequest will be set to this value under protection of the cancel spin lock. However, if *NextIrp is NULL when this routine returns, it is not necessaryly true the what is pointed to by CurrentOpRequest will also be NULL. The reason for this is that if the queue is empty when we hold the cancel spin lock, a new request may come in immediately after we release the lock. CompleteCurrent - If TRUE then this routine will complete the request pointed to by the pointer argument CurrentOpRequest. Return Value: None. --*/ { WDFREQUEST oldRequest = NULL; PREQUEST_CONTEXT reqContext; NTSTATUS status; UNREFERENCED_PARAMETER(Extension); oldRequest = *CurrentOpRequest; *CurrentOpRequest = NULL; // // Check to see if there is a new request to start up. // status = WdfIoQueueRetrieveNextRequest( QueueToProcess, CurrentOpRequest ); if(!NT_SUCCESS(status)) { ASSERTMSG("WdfIoQueueRetrieveNextRequest failed", status == STATUS_NO_MORE_ENTRIES); } *NextRequest = *CurrentOpRequest; if (CompleteCurrent) { if (oldRequest) { reqContext = SerialGetRequestContext(oldRequest); SerialCompleteRequest(oldRequest, reqContext->Status, reqContext->Information); } } }
/*++ 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: Queue - Handle to the framework queue object that is associated with the I/O request. Request - Pointer to the WDFREQUEST for the current request Length - Length of the IO operation The default property of the queue is to not dispatch zero lenght read & write requests to the driver and complete is with status success. So we will never get a zero length request. Return Value: --*/ _Use_decl_annotations_ VOID SerialEvtIoWrite( WDFQUEUE Queue, WDFREQUEST Request, size_t Length ) { PSERIAL_DEVICE_EXTENSION extension; NTSTATUS status; WDFDEVICE hDevice; WDF_REQUEST_PARAMETERS params; PREQUEST_CONTEXT reqContext; size_t bufLen; UCHAR tempIER=0x00; hDevice = WdfIoQueueGetDevice(Queue); extension = SerialGetDeviceExtension(hDevice); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, "++SerialEvtIoWrite(%p, 0x%I64x)\r\n", Request, Length); if (SerialCompleteIfError(extension, Request) != STATUS_SUCCESS) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, "--SerialEvtIoWrite 1 %Xh\r\n", (ULONG)STATUS_CANCELLED); return; } WDF_REQUEST_PARAMETERS_INIT(¶ms); WdfRequestGetParameters(Request, ¶ms); // Initialize the scratch area of the request. reqContext = SerialGetRequestContext(Request); reqContext->MajorFunction = params.Type; reqContext->Length = (ULONG) Length; status = WdfRequestRetrieveInputBuffer (Request, Length, &reqContext->SystemBuffer, &bufLen); if (!NT_SUCCESS (status)) { SerialCompleteRequest(Request , status, 0); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, "--SerialEvtIoWrite 2 %Xh\r\n", status); return; } SerialStartOrQueue(extension, Request, extension->WriteQueue, &extension->CurrentWriteRequest, SerialStartWrite); // enable mini Uart Tx interrupt TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, "SerialEvtIoWrite() - enable Tx interrupt\r\n"); tempIER=READ_INTERRUPT_ENABLE(extension, extension->Controller); WRITE_INTERRUPT_ENABLE(extension, extension->Controller, (tempIER | SERIAL_IER_THR)); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, "--SerialEvtIoWrite()=%X\r\n", status); return; }