none
Unexpected Surprise Removal caused by IWDFRemoteTarget::OpenFileByName method RRS feed

  • Question

  • Dir Sir:

    I am an newbie of windows driver development.
    If there is any incorrect thoughts in the following descriptions, please point it out. I will appreciate any advice very much.

    Recently, i was assigned a job to develop a windows driver used on a 8051 device that is permanently connected to a SOC chip within a Windows 8.1 tablet. Their connection interface is through UART, namely a serial port.

    My stepping stone was the Virtual Serial Driver Sample. It had been successfully run on the 8051 device. The IO request of DeviceIoControl can be intercepted correctly in the CMyQueue::OnDeviceIoControl. Then i needed to forward the IO request to the serial controller.

    The related code was referenced to  "Connecting a UMDF Peripheral Driver to a Serial Port" on the windows dev website.

    Based on that teaching, i added the CMyDevice::OnPrepareHardware to the Virtual Serial Driver Sample, as the following:

    HRESULT CMyDevice::OnPrepareHardware(
        _In_ IWDFDevice3 * pWdfDevice,
        _In_ IWDFCmResourceList * pWdfResourcesRaw,
        _In_ IWDFCmResourceList * pWdfResourcesTranslated
        )
    {
        HRESULT hr = S_OK;
    
        if (m_fInitialized == FALSE)
        {
            PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;
            PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptorRaw;
    
            ULONG resourceCount = 0;
            BOOLEAN fRequestFound = FALSE;
            LARGE_INTEGER requestId = { 0 };
            UCHAR connectionClass;
            UCHAR connectionType;
    
            if (pWdfResourcesTranslated == nullptr)
            {
                hr = E_INVALIDARG;
            }
    
            // step 1: get the connection ID from resource hub
            if (SUCCEEDED(hr))
            {
                resourceCount = pWdfResourcesTranslated->GetCount();
    
                // Loop through the resources and save the relevant ones
                for (ULONG i = 0; i < resourceCount; i++)
                {
                    pDescriptor = pWdfResourcesTranslated->GetDescriptor(i);
                    pDescriptorRaw = pWdfResourcesRaw->GetDescriptor(i);
    
                    if ((pDescriptor == nullptr) ||
                        (pDescriptorRaw == nullptr))
                    {
                        hr = E_POINTER;
                        break;
                    }
    
                    switch (pDescriptor->Type)
                    {
                    case CmResourceTypeConnection:
    
                        // Check against the expected connection types
                        connectionClass = pDescriptor->u.Connection.Class;
                        connectionType = pDescriptor->u.Connection.Type;
    
                        if ((connectionClass == CM_RESOURCE_CONNECTION_CLASS_SERIAL) &&
                            (connectionType == CM_RESOURCE_CONNECTION_TYPE_SERIAL_UART))
                        {
                            if (fRequestFound == FALSE)
                            {
                                // Save the request id
                                requestId.LowPart = pDescriptor->u.Connection.IdLowPart;
                                requestId.HighPart = pDescriptor->u.Connection.IdHighPart;
    
                                fRequestFound = TRUE;
                            }
                            else
                            {
                                hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
                                //Trace(
                                //    TRACE_LEVEL_ERROR,
                                //    "Duplicate resource found - %!HRESULT!",
                                //    hr);
                            }
                        }
    
                        break;
    
                    case CmResourceTypeInterrupt:
                        break;
    
                    default:
                        // Ignore all other descriptors
                        break;
                    }
    
                    if (FAILED(hr))
                    {
                        break;
                    }
                }
    
                if (SUCCEEDED(hr) &&
                    ((fRequestFound == FALSE) /*|| (fInterruptFound == FALSE)*/))
                {
                    hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_NOT_PRESENT);
                    //Trace(
                    //    TRACE_LEVEL_ERROR,
                    //    "Failed to find required resource - %!HRESULT!",
                    //    hr);
                }
            }
            // step 2: get the device path name
            WCHAR DevicePathBuffer[RESOURCE_HUB_PATH_CHARS] = { 0 };
            UNICODE_STRING DevicePath;
    
            if (SUCCEEDED(hr))
            {
                DevicePath.Buffer = DevicePathBuffer;
                DevicePath.Length = 0;
                DevicePath.MaximumLength = RESOURCE_HUB_PATH_SIZE;
    
                // Create the device path using the well known
                // resource hub path and the connection ID
                hr = HRESULT_FROM_NT(RESOURCE_HUB_CREATE_PATH_FROM_ID(
                    &DevicePath,
                    requestId.LowPart,
                    requestId.HighPart));
            }
            // Step 3: open remote target to UART controller
            IWDFDevice2* spDevice2;
    
            if (SUCCEEDED(hr))
            {
                hr = pWdfDevice->QueryInterface(
                    IID_PPV_ARGS(&spDevice2));
    
                if (SUCCEEDED(hr))
                {
                    hr = spDevice2->CreateRemoteTarget(
                        nullptr,
                        nullptr,
                        &m_spRemoteTarget);
    
                    if (FAILED(hr))
                    {
                        //Trace(
                        //    TRACE_LEVEL_ERROR,
                        //    "Failed to create remote target, %!HRESULT!",
                        //    hr);
                    }
                }
    
                if (SUCCEEDED(hr))
                {
                    UMDF_IO_TARGET_OPEN_PARAMS openParams;
    
                    openParams.dwShareMode = 0;
                    openParams.dwCreationDisposition = OPEN_EXISTING;
                    openParams.dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED;
    
                    // this method will lead to an unexpected surprise removal
                    hr = m_spRemoteTarget->OpenFileByName(
                        DevicePathBuffer,
                        (GENERIC_READ | GENERIC_WRITE),
                        &openParams);
    
    
                    if (FAILED(hr))
                    {
                        //Trace(
                        //    TRACE_LEVEL_ERROR,
                        //    "Failed to open remote target to SPB controller, "
                        //    "%!HRESULT!",
                        //    hr);
                    }
                }
            }
    
            if (SUCCEEDED(hr))
            {
                m_fInitialized = TRUE;
            }
        }
    
        return hr;
    }

    The connection ID was obtained successfully.  Then we wanted to use it to create the IO remote target by the name.

    When i deployed the modified driver to our target computer, we found that CMyDevice::OnReleaseHardware was invoked just after leaving CMyDevice::OnPrepareHardware. we snapped the call stack at that moment and found that a Surprise Removal occurred.

    We had tried to remove the m_spRemoteTarget->OpenFileByName. The CMyDevice::OnReleaseHardware was not invoked and the Surprise Removal did not occur.

    Since the IO remote target is necessary for use of forwarding the IO request received from the framework, our progress was hindered.

    We want to know why the Surprise Removal occurred and how to prevent it from happening.

    Ayn advice is appreciated. Thanks a lot.

    Thursday, May 8, 2014 12:05 PM

All replies

  • how do you know a surprise removal happened? based on the name of a function in the call stack? ReleaseHw() is called immediately after PrepareHw() if you return a failure code from PrepareHw(). What hr value are you returning when OpenFileByName is called?

    d -- This posting is provided "AS IS" with no warranties, and confers no rights.

    Thursday, May 8, 2014 4:45 PM
  • Dear sir:

    The evidence that the surprise removal occurred was from the call stack. When CMyDevice::OnReleaseHardware was invoked, we made a breakpoint inside it and observed the call stack. The content of the call stack is :

    Virtualserial!CMyDevice::OnReleaseHardware

    WUDFx!CWdfDevice::EvtDeviceReleaseHardware

    WUDFx!FxPkgPnp::PnpReleaseHardware

    WUDFx!FxPkgPnp::PnpEventFailedOwnHardware

    WUDFx!FxPkgPnp::PnpEnterNewState

    WUDFx!FxPkgPnp::PnpProcessEventInner

    WUDFx!FxPkgPnp::PnpProcessEvent

    WUDFx!FxPkgPnp::PnpSurpriseRemoval

    WUDFx!FxPkgPnp::Dispatch

    WUDFHost!CWudfDeviceStack::OnDispatchPnp

    WUDFHost!CWudfDeviceStack::Forward

    WUDFHost!ClpcNotification::UnloadSafeDispatchlrp

    WUDFHost!ClpcNotification::WudfPnpHandler

    WUDFHost!ClpcNotification::ProcessPnpPowerlrp

    WUDFHost!ClpcNotification::Message

    WUDFPlatform!WdfLpcPort::ProcessMessage

    WUDFPlatform!WdfLpc::RetrieveMessage

    WUDFHost!ThreadPoolWorkerThunk

    ntdll!TppExecuteWaitCallback

    ntdll!TppWorkerThread

    KERNEL32!BaseThreadInitThunk

    ntdll!RtlUserThreadStart

    From the preceding call stack, WUDFx!FxPkgPnp::PnpSurpriseRemoval was called within Framework.

    That is why we considered the Surprise Removal occurred.

    Moreover, we had carefully debugged in the CMyDevice::OnPrepareHardware step by step. Each return value was success. There was no fail code. The hr value returned from OpenFileByName was 0. The final hr value of CMyDevice::OnPrepareHardware was 0 as well. Thus it was successfully returned from CMyDevice::OnPrepareHardware without any erroneous code.

    Despite the successful return of CMyDevice::OnPrepareHardware, CMyDevice::OnReleaseHardware was invoked afterward. That was unexpected flow in our code.

    Finally, we had tried to only remove OpenFileByName and reserve the remaining code within CMyDevice::OnPrepareHardware. CMyDevice::OnReleaseHardware was not invoked and our driver can work properly, but the invocation of OpenFileByName was indispensable. That was another mystery.

    Any advice will be appreciated. Thanks a lot.

    Sincerely

    Zale Yu

    Friday, May 9, 2014 2:11 AM
  • if a pnp start is failed, a surprise remove is sent to tear down the stack. This means one of your power up callbacks is returning an error result. Do you register any other power up callbacks?

    d -- This posting is provided "AS IS" with no warranties, and confers no rights.

    Friday, May 9, 2014 3:30 AM
  • Dear Sir:

    Our driver was based on the Virtual Serial Driver Sample and incorporated with an interface of IPnpCallbackHardware2. The interface required us to implement two methods, such as OnPrepareHardware and OnReleaseHardware.

    Should we implement any other power up callbacks that you mentioned? Could you tell us which are required? We will follow your advice to address this issue.

    Is there any hint for us? Thanks a lot.

    Sincerely

    Zale Yu


    • Edited by Zale Yu Friday, May 9, 2014 5:55 AM
    Friday, May 9, 2014 3:50 AM
  • No other callbacks are required, I am asking which other callback interfaces you have implemented.

    d -- This posting is provided "AS IS" with no warranties, and confers no rights.

    Friday, May 9, 2014 6:08 AM
  • Dear Sir:

    We implemented two interfaces, i.e. IObjectCleanup and IPnpCallbackHardware2, for the device callback object.

    Should we need to implement IPnpCallback as well? As i know, its OnD0Entry methold is used to configure the device. However, our 8051 device is supposed to work well since it is powered on. The embedded firmware of 8051 should be able to let 8051 device launch automatically. Do we need to configure the 8051 device addtionally?

    For now, the possible cause is that we missed some callback functions that are neccessary. Is my understanding right?

    Make a supplement.

    We tried to implement IPnpCallback few minutes ago. We found some invoking sequences between CMyDevice::OnPrepareHardware and CMyDevice::OnReleaseHardware.

    The sequence is :

    CMyDevice::OnPrepareHardware ->CMyDevice::OnD0Entry->CMyDriver::OnDeinitialize->CMyDevice::OnD0Entry->CMyDevice::OnReleaseHardware.

    The implementations of CMyDriver::OnDeinitialize and CMyDevice::OnD0Entry are just directly return and return  hr = S_OK, respectively.

    Thanks.

    Sincerely

    Zale Yu


    • Edited by Zale Yu Friday, May 9, 2014 7:00 AM
    Friday, May 9, 2014 6:40 AM