Sample Code

Windows Driver Samples/ Bluetooth Serial HCI Bus Driver/ C++/ io.c/

/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

   IO.c

Abstract:

    This module contains routines that perform read/write IO operations.

Environment:

    Kernel mode only

Revision History:    

--*/

#include "driver.h"
#include "IO.tmh"

#pragma warning(disable:4127) // conditional expression is constant

#ifdef ALLOC_PRAGMA
#endif

VOID
CB_RequestFromBthportCancel(
    _In_ WDFREQUEST _RequestFromUpper
    )
/*++

Routine Description:

    Request from upper layer that driver owns is being canceled.  Its associated 
    Request to lower (UART) driver will be canceled and then this Request will
    be completed with STATUS_CANCELLED.      

    There are different paths for the Request from upper layer:
    
    1. Completion routine is invoked without cancellation (typical path)
    2. Cancellation routine is invoked while lower Request is pending.  The lower 
        request could be completed either
        a. Synchronously - completion routine is invoked before  
            WdfRequestCancelSentRequest() is returned in the cancellation routine; or
        b. Asynchronously - completion routine is invoked at later time after 
            WdfRequestCancelSentRequest has returned.
    3. Race conditions when both the cancelation and completion routine have independently started
        a. Cancellation routine is ahead and the request is completed with cancellation status.
        b. Completion routine is ahead and the request is completed with the status from the lower request.
 
Arguments:

    _RequestFromUpper - WDF Request to be cancelled

Return Value:

    none

--*/    
{        
    PUART_WRITE_CONTEXT TransferContext;
    WDFREQUEST  RequestToUART;
    WDFMEMORY   Memory;    
    BOOLEAN CancelSuccess;
    LONG CompletePath = REQUEST_PATH_NONE;

   
    DoTrace(LEVEL_WARNING, TFLAG_IO, ("+CB_RequestFromBthportCancel: Request(%p) from upper driver", _RequestFromUpper));

    TransferContext = GetWriteRequestContext(_RequestFromUpper);
    NT_ASSERT(TransferContext && L"TransferContext is not valid!");
   
    // Cancel the write Request that was previously submitted to its I/O target    
    RequestToUART = TransferContext->RequestToUART;     
    Memory = TransferContext->Memory;


    //
    // The below operation can return one of the following values.
    // REQUEST_PATH_NONE
    //      This value was returned due to one of the following conditions
    //      1. The completion routine was not yet run.
    //      2. The completion routine was run and it relinquished the control of completing the request from bthport to the cancel routine.
    //
    //      No matter what causes this value to be returned, this function is now responsible for completing the request from bthport.
    //
    // REQUEST_PATH_COMPLETION
    //      The completion routine was already called.
    //      The completion routine has not yet had a chance to relinquish control of completing the request from bthport.
    //
    //      This function does not have the control to complete the request from bthport.
    //
    CompletePath = InterlockedOr(&TransferContext->RequestCompletePath, REQUEST_PATH_CANCELLATION);

    if (REQUEST_PATH_NONE == CompletePath) {

        DoTrace(LEVEL_WARNING, TFLAG_IO, ("  >CancelSentRequest(%p) to IO Target", RequestToUART));    
        CancelSuccess = WdfRequestCancelSentRequest(RequestToUART);
        DoTrace(LEVEL_WARNING, TFLAG_IO, ("  <CancelSentRequest: %S", CancelSuccess ? L"Cancelled" : L"Failed"));

        // Done sending the cancel.  It can be dereferenced.
        WdfObjectDereference(RequestToUART);

        // No need to access this memory object in the cancellation code path in the completion function.    
        WdfObjectDelete(Memory);

        // Cannot access this request, including WdfRequestUnmarkCancelable(), after it has been completed.
        WdfRequestComplete(_RequestFromUpper, STATUS_CANCELLED);               
    }

}


NTSTATUS
HLP_AllocateResourceForWrite(
    _In_  WDFDEVICE   _Device,
    _In_  WDFIOTARGET _IoTargetSerial,
    _Out_ WDFREQUEST *_PRequest
    )
/*++

Routine Description:

    This helper function allocate resource to perform a write request

Arguments:

    _Device - WDF Device object
    
    _IoTargetSerial - WDF IO Target 
    
    _PRequest - WDF Request to allocate in this function

Return Value:

    NTSTATUS
    
--*/      
{
    NTSTATUS Status;
    WDF_OBJECT_ATTRIBUTES ObjAttributes;

    DoTrace(LEVEL_INFO, TFLAG_IO,("+HLP_AllocateResourceForWrite"));    

    // Create a WDF Request that will allocate a context(UART_WRITE_CONTEXT)
    WDF_OBJECT_ATTRIBUTES_INIT(&ObjAttributes);
    ObjAttributes.ParentObject = _Device; 
          
    Status = WdfRequestCreate(&ObjAttributes, 
                              _IoTargetSerial, 
                              _PRequest);
    
    if (!NT_SUCCESS(Status)) 
    {
        DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestCreate() failed %!STATUS!", Status));
        goto Done;
    } 
   
Done:
    
    return Status;    
}


VOID
HLP_FreeResourceForWrite(
    PUART_WRITE_CONTEXT _TransferContext
    )
/*++

Routine Description:

    This helper function free resource allocated to perform a write request

Arguments:

    _TransferContext - Transfer context used to perform write operation

Return Value:

    none
    
--*/      
{   
    DoTrace(LEVEL_INFO, TFLAG_IO,("+HLP_FreeResourceForWrite"));   
    
    if (_TransferContext)
    {       
        if (_TransferContext->Memory)
        {
           WdfObjectDelete(_TransferContext->Memory);
           _TransferContext->Memory = NULL;
        }  
            
        if (_TransferContext->RequestToUART)
        {
            WdfObjectDelete(_TransferContext->RequestToUART);
            _TransferContext->RequestToUART = NULL;
          
        }
    }
}


VOID
CR_WriteDeviceIO(
    _In_  WDFREQUEST  _Request,
    _In_  WDFIOTARGET  _Target,
    _In_  PWDF_REQUEST_COMPLETION_PARAMS  _Params,
    _In_  WDFCONTEXT  _Context
    )
/*++

Routine Description:

    This is the completion function for sending HCI packet to the lower layer.   
    This function can also complete the request from the upper layer; see the 
    description in the cancellation function for detail on the handling of possible
    race conditions.

    A RequestCompletionPath flag in the write Context  is used with atomic Interlocked function 
    to ensure deterministic operation in both the cancellation and this completion functions.

    If the cancellation function has been called, the WdfRequestUnmarkCancelable in the completion function will return STATUS_CANCELLED.    
    This return code is used to determine to handle the processing either as a typical completion, or as a cancellation and be in sync 
    with the cancellation function.   
    
    Here are what are performed in either situations:
    
     1. Typical completion (completion function only)
            - WdfRequestUnmarkCancelable() returns not STATUS_CANCELLED
            Exercise its typical completion code path
            - Retrieve data transfer information for success case
            - Dereference(RequestUART) - will not be accessed by cancellation function 
            - Complete(RequestFromUpper) & Delete(its Memory Object)
           
            - Delete(RequestUART)
            - Dereference(RequestFromUpper)   
            
     2. Cancellation (both functions)
            A: Cancellation Function
               WdfRequestCancelSentRequest(RequestToUART) to cancel RequestToUART
            - Dereference(RequestToUART) after cancel is sent 
            - Complete(RequestFromUpper) & Delete(its Memory Object)  
    
            B: Completion function
            WdfRequestUnmarkCancelable() returns STATUS_CANCELLED
            Exercise its cancellation code path
            - Delete(RequestToUART)
            - Dereference(RequestFromUpper)
            
     Note: Code path A & B have no synchronization object to ensure their order of execution, but reference is taken on the Requests to ensure
                that they stay valid until last access.
    
        RequestToUART - take a reference to protect against being used by the cancellation function; it is de-referenced by the
            - completion function - in its typical completion code path, or
            - cancellation function - after finishing accessing it (to sent cancel)
            
        RequestFromBthport - take a reference to protect against being completed by the cancellation function and then its context 
                                         is later accessed by the completion function; this can happen if the completion function is completed
                                         asynchronously after WdfRequestCancelSentRequest() is returned; it is de-referenced by the
            - completion function - right before it exits.       

Arguments:

    _Request - WDF Request allocated by this driver
    _Target - WDF IO Target
    _Params - Completion parameters
    _Context - Context used to process this request
    
Return Value:

    none
    
--*/
{
    NTSTATUS Status;
    PUART_WRITE_CONTEXT TransferContext;    
    PFDO_EXTENSION FdoExtension;
    WDFREQUEST RequestFromBthport;
    ULONG  BytesDataWritten = 0;    
    LONG CompletePath = REQUEST_PATH_NONE;  

    UNREFERENCED_PARAMETER(_Target);  

    Status = _Params->IoStatus.Status;
    TransferContext = (PUART_WRITE_CONTEXT) _Context; 

    DoTrace(LEVEL_INFO, TFLAG_DATA,("+CR_WriteDeviceIO: %!STATUS!, Request %p, Context %p", 
            Status, _Request, _Context));      
    
    NT_ASSERT( (Status == STATUS_SUCCESS || Status == STATUS_CANCELLED) && L"WriteHCI request failed!");     
    
    //
    // Request to be completed to upper layer.
    //
    RequestFromBthport = TransferContext->RequestFromBthport;

    //
    // The below operation can return one of the following values.
    // REQUEST_PATH_NONE
    //      This value was returned either because
    //      1. This is the normal operation for this function and the request from bthport has to be completed.
    //      2. The request from bthport has already been cancelled, but the cancellation routine has not yet been called (race condition).
    //
    //      No matter what causes this value to be returned, it is safe to call WdfRequestUnmarkCancelable on the request from bthport
    //
    // REQUEST_PATH_CANCELLATION
    //      The cancellation routine was already called.
    //
    //      This function does not have the control to complete the request from bthport.
    //
    CompletePath = InterlockedOr(&TransferContext->RequestCompletePath, REQUEST_PATH_COMPLETION);
    
    // Mark RequestFromBthPort not cancellable as it is about to be completed.
    if (REQUEST_PATH_NONE != CompletePath)
    {
        DoTrace(LEVEL_ERROR, TFLAG_IO,(" Request %p is in the process of being cancelled", RequestFromBthport));
    }    
    else
    {
        //
        // Call WdfRequestUnmarkCancelable() to check whether this request has already been cancelled.
        //
        if (STATUS_CANCELLED == WdfRequestUnmarkCancelable(RequestFromBthport)) {
            //
            // The request from bthport has already been cancelled.
            // Try to relinquish control of completing the request from bthport to the cancellation routine. It is possible that the cancellation routine
            // has already been executed. In this case, this routine will have to complete the request from bthport.
            //
            // The below operation can return one of the following values.
            // REQUEST_PATH_CANCELLATION | REQUEST_PATH_COMPLETION
            //      The cancellation routine was called. The cancellation will not complete the request, so this function will have to complete it.
            //
            // REQUEST_PATH_COMPLETION
            //      The cancellation routine has not yet been called.
            //      The InterlockedCompareExchange successfully masked the REQUEST_PATH_COMPLETE bit and so the completin routine
            //      will complete this request.
            //
            CompletePath = InterlockedCompareExchange(&TransferContext->RequestCompletePath,
                                                      REQUEST_PATH_NONE,
                                                      REQUEST_PATH_COMPLETION);

            //
            // Since the cancellation was already called and it will not complete the request, reset the value of complete to
            // REQUEST_PATH_NONE so that the request from bthport will be completed.
            //
            if (CompletePath & REQUEST_PATH_CANCELLATION) {
                CompletePath = REQUEST_PATH_NONE;
            }
        }

        if (REQUEST_PATH_NONE == CompletePath) {
            
            // Dereference this request as cancellation function is not invoked to access it.
            WdfObjectDereference(_Request);
        
            //
            // Return data transfer information to caller for success Status
            //
            if (NT_SUCCESS(Status))
            {
                WDFMEMORY ReqOutMemory = NULL;
                ULONG  BytesWritten;
                PULONG    OutBuffer = NULL;        
                size_t    OutBufferSize = 0;   

                BytesWritten =  (ULONG) _Params->Parameters.Write.Length;  

                DoTrace(LEVEL_INFO, TFLAG_DATA,("   Packet: Type %d, DataLen %d, BytesWritten %d",                   
                        TransferContext->HCIContext->Type,
                        TransferContext->HCIContext->DataLen,
                        BytesWritten));              

                NT_ASSERT(BytesWritten == TransferContext->HCIPacketLen && "Unexpected incomplete HCI Write!");
                
                if (BytesWritten != TransferContext->HCIPacketLen)
                {
                    // return a generic failure for an incomplete transfer
                    Status = STATUS_UNSUCCESSFUL;
                    goto Done;
                }
                
                //
                // return data bytes written in the OutputParameter
                //
                Status = WdfRequestRetrieveOutputMemory(RequestFromBthport, &ReqOutMemory); 
                if (NT_SUCCESS(Status)) 
                {  
                    OutBuffer = (PULONG) WdfMemoryGetBuffer(ReqOutMemory, &OutBufferSize);
                    if (OutBufferSize >= sizeof(ULONG))
                    {                    
                        // Set OutputParameter value and its size              
                        *OutBuffer = TransferContext->HCIContext->DataLen;
                        BytesDataWritten = sizeof(ULONG);                 
                    }
                }               
            }
            else 
            {
                // Return  the status as is.
            }	
        }
    }

Done:    

    if (REQUEST_PATH_NONE == CompletePath)
    {        
        // Increment the completion count based on packet type.
        FdoExtension = TransferContext->FdoExtension; 
        
        if (TransferContext->HCIContext->Type == (UCHAR) HciPacketCommand)
        {
            InterlockedIncrement(&FdoExtension->CntCommandCompleted);
        }
        else if (TransferContext->HCIContext->Type == (UCHAR) HciPacketAclData)
        {
            InterlockedIncrement(&FdoExtension->CntWriteDataCompleted);           
        }        	  

        DoTrace(LEVEL_INFO, TFLAG_IO,(" WriteDeviceIO: Request %p complete with %!STATUS! and %d BytesDataWritten",
                RequestFromBthport, Status, BytesDataWritten));   

        // Delete this memory object that is no longer needed.
        WdfObjectDelete(TransferContext->Memory);  

        // Cannot access this Request and its context after it is completed.               
        WdfRequestCompleteWithInformation(RequestFromBthport, Status, BytesDataWritten); 
 
    }
    
    // Delete this request in its completion function.   
    WdfObjectDelete(_Request);    

    // Done accessing it in this function.   This request is either completed in this function for the typical completion situation or in the cancellation function.
    WdfObjectDereference(RequestFromBthport);

    DoTrace(LEVEL_INFO, TFLAG_IO,("-CR_WriteDeviceIO"));     
}



VOID
ReadSegmentStateSet(
    PUART_READ_CONTEXT _ReadContext,
    UART_READ_STATE    _NewState
    )
/*++

Routine Description:

    This helper centralize the setting of read state. It can be used to detect 
    possible incorrect state transition.   

Arguments:

    _ReadContext - read context which has existing state
    _NewState - new read state
    
Return Value:

    none
    
--*/     
{
    UART_READ_STATE OldState = _ReadContext->ReadSegmentState;
    
    DoTrace(LEVEL_INFO, TFLAG_IO, ("+<<<< -- %s to %s state -- >>>>",
            OldState == GET_PKT_TYPE    ? "Type"    :
            OldState == GET_PKT_HEADER  ? "Header"  : 
            OldState == GET_PKT_PAYLOAD ? "Payload" : "Unknown",        
            _NewState == GET_PKT_TYPE    ? "Type"    :
            _NewState == GET_PKT_HEADER  ? "Header"  : 
            _NewState == GET_PKT_PAYLOAD ? "Payload" : "Unknown" ));

    // Validate the state transition
    switch (_NewState)
    {
        case GET_PKT_TYPE: 
            // Intialize the context for a new packet
            _ReadContext->BytesReadNextSegment = 0;
            _ReadContext->H4Packet.Type = 0;
            _ReadContext->BytesToRead4FullPacket = 0;
            RtlZeroMemory(_ReadContext->H4Packet.Packet.Raw, HCI_ACLDATA_HEADER_LEN);
            break;            
        case GET_PKT_HEADER:
        case GET_PKT_PAYLOAD:
            // Reset segment count
            _ReadContext->BytesReadNextSegment = 0;
            break;            
    }
    
    _ReadContext->ReadSegmentState = _NewState;
}

                        // Full packet: match to a Request and complete it.
NTSTATUS                        
ReadH4PacketComplete(
    PFDO_EXTENSION _FdoExtension,
    UCHAR  _Type,
    _In_reads_bytes_(_BufferLength) PUCHAR _Buffer,
    ULONG  _BufferLength
    )
{
    NTSTATUS Status = STATUS_SUCCESS;

    DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4PacketComplete %S Packet Length %d", 
        _Type == (UCHAR) HciPacketEvent ? L"Event" : L"AclData", _BufferLength ));  

#if DBG
    // Tracking last completed packet 
    RtlCopyMemory(_FdoExtension->LastPacket, _Buffer, _BufferLength);
    _FdoExtension->LastPacketLength = _BufferLength;
#endif  
                   
    if (_Type == (UCHAR) HciPacketEvent)
    {
        ReadRequestComplete(_FdoExtension, 
                            HciPacketEvent, 
                            _BufferLength,
                            _Buffer,
                            _FdoExtension->ReadEventQueue,
                            &_FdoExtension->EventQueueCount,
                            &_FdoExtension->ReadEventList, 
                            &_FdoExtension->EventListCount);                
    }
    else 
    {
        ReadRequestComplete(_FdoExtension,          
                            HciPacketAclData,
                            _BufferLength,
                            _Buffer,
                            _FdoExtension->ReadDataQueue,
                           &_FdoExtension->DataQueueCount,
                           &_FdoExtension->ReadDataList, 
                           &_FdoExtension->DataListCount);          
    }  

    DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadH4PacketComplete %!STATUS!", Status));
    
    return Status;
}


NTSTATUS
ReadH4PacketReassemble(
    _Inout_  PUART_READ_CONTEXT _ReadContext,
    _In_  ULONG  _BytesRead,
    _In_reads_bytes_(_BytesRead) PUCHAR _Buffer
    )
/*++

Routine Description:

    A function enforce a state machine to process reading data to form a 
    complete HCI packet.   

Arguments:

    _ReadContext - read context
    _BytesRead - bytes of data read and is in the output buffer
    _OutBuffer - Buffer that contain the data
    
Return Value:

    NTSTATUS
    
--*/     
{
    NTSTATUS Status = STATUS_SUCCESS;   
    ULONG BytesRemained = _BytesRead;
    PUCHAR Buffer = _Buffer;
    PFDO_EXTENSION FdoExtension = _ReadContext->FdoExtension; 
    PH4_PACKET H4Packet;
    ULONG PacketLen;
    ULONG BytesToRead;
    
    
    DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4PacketReassemble: %d _BytesRead, ReadSegmentState %d", 
        _BytesRead, _ReadContext->ReadSegmentState));    

    // 
    // By design, it will take two reads to complete an H4 packets.
    //
    // First Read (5 bytes = 1 + 4 = Type + Larger of (ACLDataHeader:4, EvetnHeader:2))
    //
    //   - Event 
    //       Complete (1 + 2    ), this is an Event packet without any param.                                    
    //       Complete (1 + 2 + 1), event with 1 param
    //         * These two outcome requires interval timeout to complete the read (ask for 5).    
    //       Complete (1 + 2 + 2), event with 2 params
    //         * if completed with one read, do the First read again.
    // 
    //       Partial  (1 + 2 + 2 + ParamCount-2), this will complete in next read
    //            BytesToRead = ParamCount - 2
    //
    //   - ACL Data
    //       Partial (1 + 4 + DataLength), this packet will be complete in next read
    //            ByteToRead = DataLength
    // Second read
    //   - Event/AclData
    //       Complete (5 + BytesToRead)    
    //    

    while (NT_SUCCESS(Status) && BytesRemained > 0) {
        
        // Process read buffer based on its read state
        switch (_ReadContext->ReadSegmentState) {
        case GET_PKT_TYPE: 
            H4Packet = (PH4_PACKET) Buffer;
            BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);
            
            if (H4Packet->Type == (UCHAR) HciPacketEvent) {
                DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] ---------- ")); 
                 _ReadContext->BytesToRead4FullPacket = HCI_EVENT_HEADER_SIZE;
            }
            else if (H4Packet->Type == (UCHAR) HciPacketAclData) {          
                DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] ---------- "));  
                 _ReadContext->BytesToRead4FullPacket = HCI_ACL_HEADER_SIZE;
            }
            else {
                //
                // Abort the read operation here but can consider to traverse the data 
                // until a valid packet type is found.            
                //
                Status = STATUS_INVALID_PARAMETER;  // discard and read again
                DoTrace(LEVEL_ERROR, TFLAG_IO, (" Unexpected PacketType %d", H4Packet->Type));  
                NT_ASSERT(FALSE && L"Detected unknown packet type");  
                goto OutOfSync;            
            }

            // Proceed to read packet header
            _ReadContext->H4Packet.Type = H4Packet->Type;    // Valid packet type is cached.          
            ReadSegmentStateSet(_ReadContext, GET_PKT_HEADER);                        
            break;
            
        case GET_PKT_HEADER:                                                  
            if (_ReadContext->H4Packet.Type == (UCHAR) HciPacketEvent) {
                if (_ReadContext->BytesReadNextSegment == 0 && BytesRemained) {
                    _ReadContext->H4Packet.Packet.Event.EventCode = *Buffer;
                    DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] Code 0x%x", _ReadContext->H4Packet.Packet.Event.EventCode));                      
                    BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);                
                    _ReadContext->BytesToRead4FullPacket = 1;  // Read the ParamsCount if needed
                }
                
                if (_ReadContext->BytesReadNextSegment == 1 && BytesRemained) {
                    _ReadContext->H4Packet.Packet.Event.ParamsCount = *Buffer;
                    DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] ParamsCount 0x%x", _ReadContext->H4Packet.Packet.Event.ParamsCount));                                  
                    BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);

                    if (_ReadContext->H4Packet.Packet.Event.ParamsCount == 0) {
                        // Full packet: match to a Request and complete it.
                        PacketLen = HCI_EVENT_HEADER_LEN + _ReadContext->H4Packet.Packet.Event.ParamsCount;                
                        DoTrace(LEVEL_INFO, TFLAG_DATA, (" [Event completed] PacketLen %d", PacketLen));
                        Status = ReadH4PacketComplete(FdoExtension,
                                                      _ReadContext->H4Packet.Type,
                                                      (PUCHAR) &_ReadContext->H4Packet.Packet.Event, 
                                                      PacketLen); 
                        // Read next packet
                        ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE); 
                    }
                    // Read the remainder of a full (Event) packet
                    else {
                        if (BytesRemained < _ReadContext->H4Packet.Packet.Event.ParamsCount) { 
                          _ReadContext->BytesToRead4FullPacket = 
                                _ReadContext->H4Packet.Packet.Event.ParamsCount - BytesRemained;
                        }
                    
                        // Process to read packet payload
                        ReadSegmentStateSet(_ReadContext, GET_PKT_PAYLOAD);
                    }
                }  
            }
            else {
                
                if (_ReadContext->BytesReadNextSegment == 0 && BytesRemained) {
                    _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer;
                    DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[0] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment]));                               
                    BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);                
                    _ReadContext->BytesToRead4FullPacket = 3;  // Read the remaining Dta header if needed
                }

                if (_ReadContext->BytesReadNextSegment == 1 && BytesRemained) {
                    _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer;
                    DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[1] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment]));                                 
                    BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);                
                    _ReadContext->BytesToRead4FullPacket = 2;  // Read the remaining Dta header if needed                    
                }                

                if (_ReadContext->BytesReadNextSegment == 2 && BytesRemained) {
                    _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer;
                    DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[2] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment]));                              
                    BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);                
                    _ReadContext->BytesToRead4FullPacket = 1;  // Read the remaining Dta header if needed                    
                }

                if (_ReadContext->BytesReadNextSegment == 3 && BytesRemained) {
                    _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer;
                    DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[3] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment]));                                  
                    BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1); 

                    // Read the reamainder of a full (Data) packet
                    if (BytesRemained < _ReadContext->H4Packet.Packet.AclData.DataLength) { 
                        _ReadContext->BytesToRead4FullPacket = 
                            _ReadContext->H4Packet.Packet.AclData.DataLength - BytesRemained;
                    }

                    // Process to read packet payload
                    ReadSegmentStateSet(_ReadContext, GET_PKT_PAYLOAD);   
                }                                                
            }           
            break;
            
        case GET_PKT_PAYLOAD:
            if (_ReadContext->H4Packet.Type == (UCHAR) HciPacketEvent) { 
                
                BytesToRead = _ReadContext->H4Packet.Packet.Event.ParamsCount - _ReadContext->BytesReadNextSegment;
                
                if (BytesRemained >= BytesToRead) {
                    // Full packet
                    RtlCopyMemory(&_ReadContext->H4Packet.Packet.Event.Params[_ReadContext->BytesReadNextSegment], 
                                  Buffer, 
                                  BytesToRead);
                    DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] Payload[%d + %d] = FULL", 
                            _ReadContext->BytesReadNextSegment, 
                            BytesToRead));                                  
                    BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesToRead);  
                    
                    // Full packet: match to a Request and complete it.
                    PacketLen = HCI_EVENT_HEADER_LEN + _ReadContext->H4Packet.Packet.Event.ParamsCount;                
                    Status = ReadH4PacketComplete(FdoExtension,
                                                  _ReadContext->H4Packet.Type,
                                                  (PUCHAR) &_ReadContext->H4Packet.Packet.Event, 
                                                  PacketLen);               
                    // Read next packet
                    ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE);                    
                }
                else {
                    // Partial packet
                    RtlCopyMemory(&_ReadContext->H4Packet.Packet.Event.Params[_ReadContext->BytesReadNextSegment], 
                                  Buffer, 
                                  BytesRemained);
                    DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] Payload[%d + %d] = Partial; %d to read", 
                            _ReadContext->BytesReadNextSegment,
                            BytesRemained,
                            BytesToRead - BytesRemained));                     
                    _ReadContext->BytesReadNextSegment += BytesRemained;                    
                    BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesRemained);   
                    
                    // Remaining event params to read
                    _ReadContext->BytesToRead4FullPacket = 
                        _ReadContext->H4Packet.Packet.Event.ParamsCount - _ReadContext->BytesReadNextSegment;
                }                
            }
            else {

                if (_ReadContext->H4Packet.Packet.AclData.DataLength > HCI_MAX_ACL_PAYLOAD_SIZE) {
                    Status = STATUS_INVALID_PARAMETER;  // discard and read again
                    DoTrace(LEVEL_ERROR, TFLAG_IO, (" Unexpected ACL DataLength %d > Presetted maximum size %d", 
                            _ReadContext->H4Packet.Packet.AclData.DataLength,
                            HCI_MAX_ACL_PAYLOAD_SIZE));
                    NT_ASSERT(FALSE && L"Max ACL DataLength exceeded the presetted Max"); 
                    goto OutOfSync;   
                }
                
                BytesToRead = _ReadContext->H4Packet.Packet.AclData.DataLength - _ReadContext->BytesReadNextSegment;
                
                if (BytesRemained >= BytesToRead) {
                    // Process full packet
                    RtlCopyMemory(&_ReadContext->H4Packet.Packet.AclData.Data[_ReadContext->BytesReadNextSegment], 
                                  Buffer, 
                                  BytesToRead);
                    DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Payload[%d + %d] = FULL", 
                            _ReadContext->BytesReadNextSegment,
                            BytesToRead));                     
                    BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesToRead);  
                    
                    // Full packet: try match to a Request in queue (if any) and complete it.
                    PacketLen = HCI_ACLDATA_HEADER_LEN + _ReadContext->H4Packet.Packet.AclData.DataLength;                
                    Status = ReadH4PacketComplete(FdoExtension,
                                                  _ReadContext->H4Packet.Type,
                                                  (PUCHAR) &_ReadContext->H4Packet.Packet.AclData, 
                                                  PacketLen);               
                    // Next packet
                    ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE);                    
                }
                else {
                    // Process partial packet
                    RtlCopyMemory(&_ReadContext->H4Packet.Packet.AclData.Data[_ReadContext->BytesReadNextSegment], 
                                  Buffer, 
                                  BytesRemained);
                    DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Payload[%d + %d] = Partial; %d to read", 
                            _ReadContext->BytesReadNextSegment,
                            BytesRemained,
                            BytesToRead - BytesRemained)); 
                    _ReadContext->BytesReadNextSegment += BytesRemained;                     
                    BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesRemained);                   
                    
                    // Remaining data to read
                    _ReadContext->BytesToRead4FullPacket = 
                        _ReadContext->H4Packet.Packet.AclData.DataLength - _ReadContext->BytesReadNextSegment;                     
                }                 
            }                           
            break;    
            
        default:
            DoTrace(LEVEL_ERROR, TFLAG_IO, (" Unknown ReadSegmentState"));
            break;
        }
    }

    return Status;
    
OutOfSync:

    DoTrace(LEVEL_ERROR, TFLAG_IO, (" Out-of-sync error detected in ProcessReadBuffer() %!STATUS!", Status));
    
    return Status;
}


VOID
ReadH4PacketCompletionRoutine(
    _In_  WDFREQUEST   _Request,
    _In_  WDFIOTARGET  _Target,
    _In_  PWDF_REQUEST_COMPLETION_PARAMS  _Params,
    _In_  WDFCONTEXT  _Context
    )
/*++

Routine Description:

    This is CR function for reading data from device.  It process the data read and 
    send down another request unless there is an error or the request is being 
    canceled.

Arguments:

    _Request - a caller allocated WDF Request
    _Target - WDF IO Target
    _Params - Completion parameters
    _Context - Context of this request
    
Return Value:

    none
    
--*/      
{
    NTSTATUS Status;    
    PUART_READ_CONTEXT ReadContext;
    PFDO_EXTENSION FdoExtension;
    ULONG BytesRead;
    WDFMEMORY ReadMemory;
    PUCHAR  OutBuffer;
    size_t  OutBufferSize;
    READ_REQUEST_STATE PreviousState;
    

    UNREFERENCED_PARAMETER(_Request);      
    UNREFERENCED_PARAMETER(_Target);      

    // Operation result    
    Status = _Params->IoStatus.Status;
    BytesRead =  (ULONG) _Params->Parameters.Read.Length;
    
    ReadContext = (PUART_READ_CONTEXT) _Context;
    ReadContext->Status = Status;
    
    // Set to REQUEST_COMPLETE if skip REQUEST_PENDING state.       
    PreviousState = InterlockedCompareExchange((PLONG)&ReadContext->RequestState,
                                               REQUEST_COMPLETE,
                                               REQUEST_SENT); 

    DoTrace(LEVEL_WARNING, TFLAG_DATA, ("+ReadH4PacketCompletionRoutine %!STATUS! %d BytesRead %S)", 
            Status, BytesRead, PreviousState == REQUEST_PENDING ? L"Async" : L"*Sync*"));  

    FdoExtension = (PFDO_EXTENSION) ReadContext->FdoExtension;     
    
    //
    // The return status can either be 
    //      - successful (buffer completely filled), 
    //      - timeout (buffer not completed filled prior to interval timeout expired
    //      - cancellation 
    //      - failure
    //
    if (NT_SUCCESS(Status) || Status == STATUS_IO_TIMEOUT || Status == STATUS_TIMEOUT) {
        // Continue to process         
    }
    else  {
        DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4PacketCompletionRoutine failed %!STATUS!", Status));
        if (Status == STATUS_CANCELLED) {
            //
            // Under regualr operational state, IO Target will only cancel a request 
            // when it is ready to abort (e.g. device removal). 
            //                  
        }
        
        goto Exit;
    }
         
    ReadMemory = _Params->Parameters.Read.Buffer;
    OutBuffer = (PUCHAR) WdfMemoryGetBuffer(ReadMemory, &OutBufferSize); 
    NT_ASSERT(OutBufferSize >= BytesRead);
    DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4PacketCompletionRoutine %d BytesRead pBuffer %p", BytesRead, OutBuffer));    
  
    //
    // Process a read buffer if there is data
    //
    if (OutBuffer && BytesRead)
    {                     
        //
        // Process the incoming data to form partial or full H4 packet
        //
        Status = ReadH4PacketReassemble(ReadContext, 
                                        BytesRead, 
                                        OutBuffer);   

        // If data stream error, ignore the packet and start over.
        if (!NT_SUCCESS(Status))
        {
            FdoExtension->OutOfSyncErrorCount++;  
            DoTrace(LEVEL_ERROR, TFLAG_IO, (" ====> [%d] 0x%x  <=====", 
                    FdoExtension->OutOfSyncErrorCount,
                    *OutBuffer));
            NT_ASSERT(NT_SUCCESS(Status) && L"Encountered an out-of-sync condition!");
            
            // Prepare to read next data packet, starting with packet type.
            ReadSegmentStateSet(ReadContext, GET_PKT_TYPE);

            // Log(Error): log statistic of the read pump until this error

            //
            // If there is a (knonw) hardware error or if we have exceeded maximun hardware count, 
            // the link is no longer reliable.  Need to report to the upper layer via a read request.
            //
            if (FdoExtension->HardwareErrorDetected && FdoExtension->OutOfSyncErrorCount > MAX_HARDWARE_ERROR_COUNT)
            {
                //
                // Complete an event or read data request with STATUS_DEVICE_DATA_ERROR error to trigger
                // BthMini/BthPort to handle the situation.  IT can perform HCI_RESET to restore the 
                // data channel.
                //
#ifdef REPORT_HARDWARE_ERROR
                WDFREQUEST Request;

                DoTrace(LEVEL_ERROR, TFLAG_IO, (" ++++ Report a hardware error; OutOfSyncCount %d",  FdoExtension->OutOfSyncErrorCount));
                
                KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql);                   
                    // Complete a read (event or data) request with a specific error to indicate hardware error.
                    Status = WdfIoQueueRetrieveNextRequest(FdoExtension->ReadEventQueue, &Request);
                    
                    // if there is no event request, find a read data request.
                    if (Status == STATUS_NO_MORE_ENTRIES)
                    {
                        Status = WdfIoQueueRetrieveNextRequest(FdoExtension->ReadDataQueue, &Request);    
                    }
                KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); 

            
                if (NT_SUCCESS(Status))
                {
                    DoTrace(LEVEL_ERROR, TFLAG_IO, (" Complete a request with STATUS_DEVICE_DATA_ERROR"));         
                    WdfRequestComplete(Request, STATUS_DEVICE_DATA_ERROR);     
                }
#endif  // REPORT_HARDWARE_ERROR
                Status = STATUS_DEVICE_DATA_ERROR;

                // abort and stop read pump
                goto Exit;                  

            }
            else
            {

                DoTrace(LEVEL_ERROR, TFLAG_IO, (" Detect out-of-sync error but read ahead..."));
            
                // Reset hardware error.
                FdoExtension->HardwareErrorDetected = FALSE;                            

                // try next 
                goto ReadNext;  
            }            
        }        
    }
    else
    {
        NT_ASSERT(Status == STATUS_TIMEOUT);
    }

ReadNext:   

    if (PreviousState == REQUEST_PENDING)          
    {   
        ULONG BytesToRead;

        //
        // Determine what is the size of the buffer to send down.
        //
        BytesToRead = (ReadContext->ReadSegmentState == GET_PKT_TYPE ? INITIAL_H4_READ_SIZE :
                       ReadContext->BytesToRead4FullPacket ? ReadContext->BytesToRead4FullPacket :
                       sizeof(FdoExtension->ReadBuffer));

        DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4Packet(Read Buffer Size %d bytes)", BytesToRead));
            
        // Issue next read here since this request was complete asychronously 
        // i.e. pending first and then this completion routein is invoked.           
        ReadH4Packet(ReadContext,
                     FdoExtension->ReadRequest,
                     FdoExtension->ReadMemory,
                     FdoExtension->ReadBuffer,
                     BytesToRead);
    }
    else
    {
        // Fall through and leave this fucntion if this request was completed synchronously; 
        // i.e. this function is invoked first and then return to the RequestSent function.
    } 

    DoTrace(LEVEL_INFO, TFLAG_IO, ("-CR_ReadReadIO (fall though)"));   

    return;

Exit:
    
    if (!NT_SUCCESS(Status))
    {          
        NT_ASSERT(Status == STATUS_CANCELLED);                       
        FdoExtension->ReadPumpRunning = FALSE;
        DoTrace(LEVEL_WARNING, TFLAG_IO, (" Pump has stopped!"));       
    }    

    DoTrace(LEVEL_INFO, TFLAG_IO, ("-CR_ReadReadIO (error)"));     
}


NTSTATUS
ReadH4Packet(
    _In_  PUART_READ_CONTEXT _ReadContext,
    _In_  WDFREQUEST         _WdfRequest,
    _In_  WDFMEMORY          _WdfMemory,
    _Pre_notnull_ _Pre_writable_byte_size_ (_BufferLen) PVOID _Buffer,
    _In_  ULONG              _BufferLen
    )
/*++

Routine Description:

    Initiate the reading of an HCI packet (event or data) by sending down a read request.

Arguments:

    _ReadContext - Context used for reading data from target UART device

Return Value:

    NTSTATUS
    
--*/    
{
    PFDO_EXTENSION   FdoExtension;     
    WDF_REQUEST_REUSE_PARAMS RequestReuseParams;      
    NTSTATUS Status;       


    DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4Packet"));

    FdoExtension = _ReadContext->FdoExtension; 

    if (0 == _BufferLen) {
        DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4Packet: _BufferLen cannot be 0"));
        Status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    while (TRUE) {
        
        DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4Packet - <start>"));
        NT_ASSERT(_ReadContext->RequestState != REQUEST_SENT);        
        
        if (!IsDeviceInitialized(FdoExtension)) {
            Status = STATUS_DEVICE_NOT_READY;
            DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4Packet: cannot attach IO %!STATUS!", Status));
            goto Done;
        }

        //
        // Issue a read event request
        //
        WDF_REQUEST_REUSE_PARAMS_INIT(&RequestReuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS);    
        Status = WdfRequestReuse(_WdfRequest, &RequestReuseParams);
        if (!NT_SUCCESS(Status)) {
            DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestReuse failed %!STATUS!", Status));
            goto Done;
        } 
        
        Status = WdfMemoryAssignBuffer(_WdfMemory, _Buffer, _BufferLen);
        if (!NT_SUCCESS(Status)) {
            DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryAssignBuffer failed %!STATUS!", Status));
            goto Done;
        }
          
        Status = WdfIoTargetFormatRequestForRead(FdoExtension->IoTargetSerial,
                                                 _WdfRequest,
                                                 _WdfMemory,
                                                 NULL, NULL);
                   
        if (!NT_SUCCESS(Status)) {
            DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoTargetFormatRequestForRead failed %!STATUS!", Status));             
            goto Done;
        }
        
        // Note: This request is sent to UART driver so it cannot be marked cancellable.
        // But it can be canceled by issuing WdfRequestCancelSentRequest().
       
        WdfRequestSetCompletionRoutine(_WdfRequest,
                                       ReadH4PacketCompletionRoutine,
                                       _ReadContext); 
        
        InterlockedExchange((PLONG)&_ReadContext->RequestState, REQUEST_SENT);

        if (FALSE == WdfRequestSend(_WdfRequest,        
                                    FdoExtension->IoTargetSerial,
                                    WDF_NO_SEND_OPTIONS))
        {
            Status = WdfRequestGetStatus(_WdfRequest);
            DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestSend failed %!STATUS!", Status));

            // Not much we can do if cannot send this request; data pump will be stopped!              
            goto Done;
        }
        else
        {        
            READ_REQUEST_STATE PreviousState;

            // Set to REQUEST_PENDING if it is in the REQUEST_SENT state.
            PreviousState = InterlockedCompareExchange((PLONG) &_ReadContext->RequestState,
                                                       REQUEST_PENDING,
                                                       REQUEST_SENT);

            DoTrace(LEVEL_WARNING, TFLAG_IO, (" WdfRequestSend ReqState: %d -> %d",
                    PreviousState, _ReadContext->RequestState)); 

            if (PreviousState == REQUEST_SENT)              
            {
                // Request is still pending, and will be completed asychronously in the 
                // completion routine where it can issue next read.
                Status = STATUS_PENDING; 
                break;
            }
            else 
            {
                Status = FdoExtension->ReadContext.Status;
                if (NT_SUCCESS(Status))
                {
                    // Previous request has been complete synchronously in the 
                    // completion routine; do next read in this function.                    
                }
                else
                {
                    // No tolerance for error
                    break;
                }
            }
        }
    }

Done:

    if (!NT_SUCCESS(Status))
    {
        NT_ASSERT(Status == STATUS_CANCELLED);                 
        FdoExtension->ReadPumpRunning = FALSE;
    } 
    
    DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadH4Packet %!STATUS!", Status));

    return Status;
}

__inline 
PHCI_PACKET_ENTRY
HLP_CreatePacketEntry(
    _In_ ULONG  _PacketLength,     
    _In_reads_bytes_(_PacketLength) PUCHAR _Packet
    )
{
    PHCI_PACKET_ENTRY  PacketEntry = NULL;
    
    PacketEntry = (PHCI_PACKET_ENTRY)ExAllocatePool(NonPagedPoolNx, sizeof(HCI_PACKET_ENTRY) + _PacketLength);
    if (PacketEntry != NULL) {
        InitializeListHead(&PacketEntry->DataEntry);
        RtlCopyMemory(PacketEntry->Packet, _Packet, _PacketLength); 
        PacketEntry->PacketLen = _PacketLength;
    }
    
    return PacketEntry;
}

NTSTATUS
ReadRequestComplete(
    _In_ PFDO_EXTENSION _FdoExtension,
    _In_ UCHAR          _PacketType,
    _In_ ULONG          _PacketLength,     
    _In_reads_bytes_opt_(_PacketLength) PUCHAR _Packet,     
    _Inout_ WDFQUEUE    _Queue,
    _Inout_ PLONG       _QueueCount,    
    _Inout_ PLIST_ENTRY _ListHead,
    _Inout_ PLONG       _ListCount
    )
/*++
Routine Description:

    This helper function processes both complete HCI Data packet from the device to find 
    a pending Request, or find a completed HCI packet in a list to complete a Request.

Arguments:

    _FdoExtension - Device context
    _PacketType - HCI packet type (either Event or Data for incoming data)
    _ListHead - List where to retrieve completed HCI packet
    _Request - Request that is used to complete a read if a corresponding HCI packet is available.

Return Value:

    NTSTATUS - STATUS_SUCCESS Or STATUS_INSUFFICIENT_RESOURCE

--*/     
{
    KIRQL  Irql;    
    WDFREQUEST  Request = NULL;
    NTSTATUS    Status = STATUS_SUCCESS;
    PHCI_PACKET_ENTRY  PacketEntry = NULL;
    WDFMEMORY ReqOutMemory;
    size_t BufferSize = 0, BytesToReturn;
    PBTHX_HCI_READ_WRITE_CONTEXT HCIContext;
    BOOLEAN CompleteRequest = FALSE;

    DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadRequestComplete"));       

    // 
    //      (ReqQueue, PktList)
    //  C0. ( empty,   empty) -> Add packet to list
    //  C1. ( empty,  !empty) -> Add packet to list
    //  C2. (!empty,   empty) -> DequeueAndCompletRequest(Packet)
    //  C3. (!empty,  !empty) -> Error! Cannot both empty at this function entry.
    //

    KeAcquireSpinLock(&_FdoExtension->QueueAccessLock, &Irql);    

    if (_Packet) {
        
        Status = WdfIoQueueRetrieveNextRequest(_Queue, &Request); 
        if (Status == STATUS_SUCCESS) {
            // Case 2: Typical code path
            InterlockedDecrement(_QueueCount);    
            DoTrace(LEVEL_INFO, TFLAG_IO, (" (C2) Complete a request %p, _Packet %p, _PacketLength %d", 
                Request, _Packet, _PacketLength)); 

            CompleteRequest = TRUE;
            
            // Case 3: An error condition if List is not empty
            NT_ASSERT(IsListEmpty(_ListHead));
        }
        else {
            // Case 0:
            PacketEntry = HLP_CreatePacketEntry(_PacketLength, _Packet);
            if (PacketEntry == NULL) {
                // Error condition
                Status = STATUS_INSUFFICIENT_RESOURCES;
                DoTrace(LEVEL_ERROR, TFLAG_IO, (" (C0/Error) Could not allocate HCI_PACKET_ENTRY %!STATUS!", Status));     
                // This packet will be dropped; but nothing we can do as system resource is depleted!
            }  
            else {
                // Cache this packet to Packet List
                InsertTailList(_ListHead, &PacketEntry->DataEntry);
                InterlockedIncrement(_ListCount);
                DoTrace(LEVEL_INFO, TFLAG_IO, (" (C0) Queuing packet with list count %d", *_ListCount));                
            }
        }
    } 
    else {        
        if (!IsListEmpty(_ListHead)) { 
            Status = WdfIoQueueRetrieveNextRequest(_Queue, &Request); 
            if (Status == STATUS_SUCCESS) {
                // Case 2: Has Packet in the list while a new request arrives
                InterlockedDecrement(_QueueCount);        
                
                PacketEntry = (PHCI_PACKET_ENTRY) RemoveHeadList(_ListHead);
                _Packet = PacketEntry->Packet;
                _PacketLength = PacketEntry->PacketLen;               
                InterlockedDecrement(_ListCount); 
                
                DoTrace(LEVEL_INFO, TFLAG_IO, (" (C2) Complete a request %p, _Packet %p, _PacketLength %d", 
                    Request, _Packet, _PacketLength));                 

                CompleteRequest = TRUE;
            }
            else {
                NT_ASSERT(FALSE && L"Failed to retrieve a request just queued!");
            }
        }
        else {
            // Case 1: Request is pre-pening and queued.
            Status = STATUS_PENDING;
            DoTrace(LEVEL_INFO, TFLAG_IO, (" (C1) Read request is queued"));
        } 
   }

    KeReleaseSpinLock(&_FdoExtension->QueueAccessLock, Irql); 
    
    if (!CompleteRequest) {
        goto Done;
    }          
   
    // Complete this request     
    Status = WdfRequestRetrieveOutputMemory(Request, &ReqOutMemory);            
    if (Status != STATUS_SUCCESS) {    
        DoTrace(LEVEL_ERROR, TFLAG_IO, (" Could not retrieve output buffer"));
        WdfRequestCompleteWithInformation(Request, Status, (ULONG_PTR)0);
        goto Done;
    }

    HCIContext = WdfMemoryGetBuffer(ReqOutMemory, &BufferSize);
    BytesToReturn = FIELD_OFFSET(BTHX_HCI_READ_WRITE_CONTEXT, Data) + _PacketLength;

    // This should not happen because BthMini should have sent down largest buffer according to device's capability.       
    NT_ASSERT(BytesToReturn <= BufferSize);  
         
    // Transfer data to Request's output buffer
    HCIContext->Type    = _PacketType;
    HCIContext->DataLen = _PacketLength;    
    if (BytesToReturn <= BufferSize) {
        RtlCopyMemory(&HCIContext->Data, _Packet, _PacketLength);  
    }
    else {
        Status = STATUS_BUFFER_TOO_SMALL;        
        BytesToReturn = 0;
    }
    
    // Validate and print out (WPP) HCI packet info
    HCIContextValidate(HCIContext->Type == (UCHAR) HciPacketEvent ? 
                       _FdoExtension->CntEventCompleted : _FdoExtension->CntReadDataCompleted,
                       HCIContext);

    //
    // Release memory allocated for a completed packet entry; it was not removed from the packet list.
    //
    if (PacketEntry) {
        ExFreePool(PacketEntry); 
    }

    if (HCIContext->Type == (UCHAR) HciPacketEvent) {
        InterlockedIncrement(&_FdoExtension->CntEventCompleted);
        DoTrace(LEVEL_INFO, TFLAG_DATA, (" [%d] HciPacketEvent completing %!STATUS!, %d BytesToReturn",
                _FdoExtension->CntEventCompleted, Status, (ULONG) BytesToReturn));          
    }
    else if (HCIContext->Type == (UCHAR) HciPacketAclData) {
        InterlockedIncrement(&_FdoExtension->CntReadDataCompleted); 
        DoTrace(LEVEL_INFO, TFLAG_DATA, (" [%d] HciPacketAclData completing %!STATUS!, %d BytesToReturn",
                _FdoExtension->CntReadDataCompleted, Status, (ULONG) BytesToReturn));          
    }       

    DoTrace(LEVEL_INFO, TFLAG_IO, (" Completing Request(%p) %!STATUS!, %d BytesToReturn",
            Request, Status, (ULONG) BytesToReturn));    

    //
    // return only the actual data read, not including BTHX_HCI_READ_WRITE_CONTEXT
    //
    WdfRequestCompleteWithInformation(Request, Status, BytesToReturn); 
    
Done:   
    
    DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadRequestComplete: %!STATUS!", Status));  

    return Status;
}


VOID
ReadResourcesFree(
    _In_  WDFDEVICE _Device
)
/*++
Routine Description:

    This helper function free resource allocated in its corresponding allocation
    function.

Arguments:

    _Device - WDF Device object

Return

    VOID

--*/ 
{
    PFDO_EXTENSION     FdoExtension;
    KIRQL              Irql;    

    DoTrace(LEVEL_INFO, TFLAG_IO,("+ReadResourcesFree"));

    FdoExtension = FdoGetExtension(_Device);

    //
    // Note: The Request(s) in WDFQUEUE (Event and ReadData) WDFQUEUEs 
    // are managed by WDF, which will dequeue and cancel them for us.
    // WdfIoQueueRetrieveNextRequest() returns STATUS_WDF_PAUSED since this 
    // function is invoked after entered D0.
    //

    //
    // Free resources allocated earlier
    //
    
    while(!IsListEmpty(&FdoExtension->ReadEventList)) 
    {
        PHCI_PACKET_ENTRY  PacketEntry;    

        KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql);          
            PacketEntry = (PHCI_PACKET_ENTRY)RemoveHeadList(&FdoExtension->ReadEventList);
            InterlockedDecrement(&FdoExtension->EventListCount);            
        KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql);               
        
        if (PacketEntry)
        {
            ExFreePool(PacketEntry);
            PacketEntry = NULL;
        }
    }
    NT_ASSERT(FdoExtension->EventListCount == 0);

    while(!IsListEmpty(&FdoExtension->ReadDataList)) 
    {
        PHCI_PACKET_ENTRY  PacketEntry;    

        KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql);          
            PacketEntry = (PHCI_PACKET_ENTRY)RemoveHeadList(&FdoExtension->ReadDataList);
            InterlockedDecrement(&FdoExtension->DataListCount);             
        KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql);               
        
        if (PacketEntry)
        {
            ExFreePool(PacketEntry);
            PacketEntry = NULL;
        }
    }   
    NT_ASSERT(FdoExtension->DataListCount == 0);
    
    if (FdoExtension->ReadRequest)
    {
        WdfObjectDelete(FdoExtension->ReadRequest);
        FdoExtension->ReadRequest = NULL;
    }   
}


NTSTATUS
ReadResourcesAllocate(
    _In_  WDFDEVICE _Device
)
/*++
Routine Description:

    This helper function allocates resource (queues and lists) for managing read IOs
    Request from upper layer or for data pump with the device.

Arguments:

    _Device - WDF Device object

Return Value:

    NTSTATUS - STATUS_SUCCESS Or STATUS_INSUFFICIENT_RESOURCE

--*/ 
{
    NTSTATUS  Status;
    PFDO_EXTENSION   FdoExtension;
    WDF_IO_QUEUE_CONFIG QueueConfig;
    WDF_OBJECT_ATTRIBUTES ObjAttributes;

    DoTrace(LEVEL_INFO, TFLAG_IO,("+ReadResourcesAllocate"));    

    FdoExtension = FdoGetExtension(_Device);
     

    // HCI_EVENT 
    //  Create WDF Queue for pending Read Event Request(s), and 
    //  Initialize a List for pre-fetched Event 
    WDF_IO_QUEUE_CONFIG_INIT(&QueueConfig,
                             WdfIoQueueDispatchManual);

    Status = WdfIoQueueCreate(_Device,
                              &QueueConfig,
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &FdoExtension->ReadEventQueue);

    if (!NT_SUCCESS(Status)) 
    {
        DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoQueueCreate(Event) %!STATUS!", Status));
        goto Done;
    }
    
    InitializeListHead(&FdoExtension->ReadEventList);
    
    FdoExtension->EventListCount = 0;    
    FdoExtension->EventQueueCount  = 0;

        
    // HCI_DATA 
    //  Create WDF Queue for pending Read Data Request(s), and 
    //  Initialize a List for pre-fetched Data     
    Status = WdfIoQueueCreate(_Device,
                              &QueueConfig,
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &FdoExtension->ReadDataQueue);

    if (!NT_SUCCESS(Status)) 
    {
        DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoQueueCreate(Data) %!STATUS!", Status));
        goto Done;
    }
    
    InitializeListHead(&FdoExtension->ReadDataList); 
    
    FdoExtension->DataListCount = 0;     
    FdoExtension->DataQueueCount = 0;    


    // Track request from top and HCI packets from device
    FdoExtension->CntCommandReq         = 0;
    FdoExtension->CntCommandCompleted   = 0;

    FdoExtension->CntEventReq           = 0;
    FdoExtension->CntEventCompleted     = 0;    
    
    FdoExtension->CntWriteDataReq       = 0;
    FdoExtension->CntWriteDataCompleted = 0;    
    
    FdoExtension->CntReadDataReq        = 0;
    FdoExtension->CntReadDataCompleted  = 0;


    // Create a WDF Request
    WDF_OBJECT_ATTRIBUTES_INIT(&ObjAttributes);
    ObjAttributes.ParentObject = _Device; 
    
    Status = WdfRequestCreate(&ObjAttributes, 
                              FdoExtension->IoTargetSerial, 
                              &FdoExtension->ReadRequest);
    
    if (!NT_SUCCESS(Status)) 
    {
        DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestCreate(ReadRequest) failed %!STATUS!", Status));
        goto Done;
    }     

    // Initialize the ReadContext and its initial ReadSegmentState
    RtlZeroMemory(&FdoExtension->ReadContext, sizeof(UART_READ_CONTEXT));    
    FdoExtension->ReadContext.FdoExtension = FdoExtension;
    ReadSegmentStateSet(&FdoExtension->ReadContext, GET_PKT_TYPE); 

    Status = WdfMemoryCreatePreallocated(&ObjAttributes, 
                                         &FdoExtension->ReadBuffer,
                                         sizeof(FdoExtension->ReadBuffer), 
                                         &FdoExtension->ReadMemory);   

    if (!NT_SUCCESS(Status)) 
    {
        DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryCreatePreallocated(ReadMemory) failed %!STATUS!", Status));
        goto Done;
    }

Done:

    DoTrace(LEVEL_INFO, TFLAG_IO,("-ReadResourcesAllocate %!STATUS!", Status));  
    if (!NT_SUCCESS(Status))
    {
        ReadResourcesFree(_Device);       
    }

    return Status;
}


Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS