移动宽带设备固件更新

本文向移动宽带 (MB) 模块制造商提供指导,这些模块制造商旨在通过 Windows Update (WU) 支持固件升级设备。 设备必须符合 USB-IF 设备工作组发布的 USB NCM 移动宽带接口模型 (MBIM) V1.0 规范

本文中的信息适用于:

  • Windows 8/Windows 10/Windows 11

设备要求

若要支持使用 Windows Update 的移动宽带上的固件更新,模块或设备制造商需要满足以下要求:

  • 由模块或设备制造商开发并与 INF 文件和固件有效负载一起打包的基于 UMDF(用户模式驱动程序框架)的驱动程序。 本文档后面部分提供了示例 INF 文件和详细信息
  • 用于实施以下功能的设备固件:
    • 固件 ID 设备服务 (FID)。 有关详细信息,请参阅 FID 设备服务
    • 用于支持固件更新设备服务的固件。 这是一项特定于设备制造商的设备服务,将使 UMDF 驱动程序能够调用并执行/下载固件有效负载并启动固件更新过程。

操作概述

下图显示了涉及的三个组件之间的简要设计和交互:MBIM 设备、Windows 8 操作系统和 IHV 提供的固件升级驱动程序。

显示 MBIM 设备、Windows 8 OS 和 IHV 提供的固件升级驱动程序之间的交互的图表。

  • 当 WWAN 服务检测到新 MB 设备到达时,它会检查设备是否支持固件 ID (FID) 设备服务。 如果存在,它将检索定义为 GUID 的 FID。 下面介绍了设备需要支持 IHV 的固件设备服务规范。
  • WWAN 服务 (Windows OS) 使用上面获得的 FID 作为设备硬件 ID 来生成“软设备节点”。这在上图中称为“软开发节点”。 开发节点的创建将启动 PnP 子系统 (Windows OS),以查找最匹配的驱动程序。 在 Windows 8 中,PnP 系统将首先尝试从本地存储安装驱动程序(如果可用),并且在并行 OS 中将尝试从 WU 提取更匹配的驱动程序。 如果没有更匹配的驱动程序可用于解决“未找到驱动程序”问题,则会将收件箱 NULL 驱动程序用作默认值。
  • 基于 FID 匹配项的 IHV WU 包将被拉取到计算机并进行安装。 预计 FID 表示唯一的固件 SKU(此处由组合设备 VID/PID/REV 和 MNO 定义的唯一性)。 WU 包将包含 IHV 创作的 UMDF 驱动程序以及固件有效负载。
  • 在软开发节点上加载 IHV UMDF 后,它将负责控制固件更新流。 应该注意的是,软开发节点的生存期与 MBIM 设备的物理存在有关。 UMDF 驱动程序应执行以下步骤,才能执行固件更新
    • 设备在固件更新过程中重启多次是可接受的,但会导致卸载/重新加载 UMDF 驱动程序
    • 整个固件升级过程(包括重启)应该不超过 60 秒。
    • 完成固件更新且设备已恢复为 MBIM 模式后,应通知 Windows。 这可通过清除以前设置的 DEVPKEY_Device_PostInstallInProgress 属性来完成。 IWDFUnifiedPropertyStore 接口介绍如何在开发节点上设置属性。 可以使用 DEVPROP_TYPE_EMPTY 清除以前设置的属性。
    • 在 OnPrepareHardware UMDF 回调期间,UMDF 驱动程序应检查是否需要更新设备上的固件。 这可通过将设备上的固件版本与通过 Windows Update 传入的固件版本进行比较来完成。 本文档后面提供了有关固件二进制文件放置位置的其他指导。 如果需要固件更新,UMDF 驱动程序应:
      • 计划工作项。 实际固件升级在工作项的上下文中进行。
      • 成功计划工作项后,通知 Windows 固件更新的开始时间。 为此,可在 OnPrepareHardware UMDF 回调的上下文中,在软开发节点上设置 DEVPKEY_Device_PostInstallInProgress 属性。
      • 正在进行固件更新时,务必不要阻止 OnPrepareHardware 回调。 预计 OnPrepareHardware 回调最长在一两秒内完成。

WU 包的示例 INF 文件

本部分提供了一个属于 WU 包的示例 INF。 INF 文件中要注意的要点包括:

  • 固件二进制文件独立于 UMDF 驱动程序。
  • 固件二进制文件位于驱动程序存储目录中,路径由操作系统确定,并使用 DIRID 13 在 INF 中引用。 二进制文件不能是包含 PE/COFF 标头的可执行文件。
  • %13%\<UniqueBinaryName>.bin
  • INF 文件将此位置存储在注册表中,而 UMDF 驱动程序将读取该注册表值来发现二进制文件位置。
  • 以下示例 INF 模板突出显示 IHV 需要填充的项。
[Version]
Signature       = "$WINDOWS NT$"
Class           = Firmware
ClassGuid       = {f2e7dd72-6468-4e36-b6f1-6488f42c1b52}
Provider        = %Provider%
DriverVer       = 06/21/2006,6.2.8303.0
CatalogFile     = MBFWDriver.cat
PnpLockdown     = 1

[Manufacturer]
%Mfg%           = Firmware,NTx86

[Firmware.NTx86]
%DeviceDesc%    = Firmware_Install,MBFW\{FirmwareID}    ; From Device Service
;%DeviceDesc%    = Firmware_Install,MBFW\{2B13DD42-649C-3442-9E08-D85B26D7825C}

[Firmware_Install.NT]
CopyFiles       = FirmwareDriver_CopyFiles,FirmwareImage_CopyFiles

[Firmware_Install.NT.HW]
AddReg          = Device_AddReg

[Device_AddReg]
HKR,,FirmwareBinary,,"%13%\MBIHVFirmware-XYZ-1.0.bin"

[Firmware_Install.NT.Services]
AddService      = WUDFRd,0x000001fa,WUDFRD_ServiceInstall

[WUDFRD_ServiceInstall]
DisplayName     = %WudfRdDisplayName%
ServiceType     = 1
StartType       = 3
ErrorControl    = 1
ServiceBinary   = %12%\WUDFRd.sys
LoadOrderGroup  = Base

[Firmware_Install.NT.CoInstallers]
CopyFiles       = WudfCoInstaller_CopyFiles

[WudfCoInstaller_AddReg]
HKR,,CoInstallers32,0x00010000,"WUDFCoinstaller.dll"

[Firmware_Install.NT.Wdf]
UmdfService      = MBIHVFirmwareDriver,MBIHVFirmwareDriver_Install
UmdfServiceOrder = MBIHVFirmwareDriver

[MBIHVFirmwareDriver_Install]
UmdfLibraryVersion  = 1.11
ServiceBinary       = %12%\UMDF\MBFWDriver.dll
DriverCLSID         = {<DriverClassGuid>} ; From UMDF driver

[FirmwareImage_CopyFiles]
MBIHVFirmware-XYZ-1.0.bin   ; Firmware Image

[FirmwareDriver_CopyFiles]
MBFWDriver.dll          ; UMDF driver for SoftDevNode

[DestinationDirs]
FirmwareImage_CopyFiles  = 13      ; Driver Store
FirmwareDriver_CopyFiles = 12,UMDF ;%SystemRoot%\System32\drivers\UMDF

[SourceDisksFiles]
MBIHVFirmware-XYZ-1.0.bin = 1

[SourceDisksNames]
1 = %DiskName%

; ================== Generic ==================================

[Strings]
Provider        = "MBIHV"
Mfg             = "MBIHV"
DeviceDesc      = "MBIHV Mobile Broadband Firmware Device"
DiskName        = "Firmware Driver Installation Media"

固件标识设备服务 (FID 设备服务)

通过 CID_MBIM_DEVICE_SERVICES 查询时,MBIM 兼容设备将实施并报告以下设备服务。 现有已知服务在部分 10.1 的 NCM MBIM 规范中定义。 Microsoft Corporation 将扩展此项来定义以下服务。

服务名称 = Microsoft 固件 ID

UUID = UUID_MSFWID UUID

值 = e9f7dea2-feaf-4009-93ce-90a3694103b6

具体而言,为 UUID_MSFWID 设备服务定义了以下 CID:

CID = CID_MBIM_MSFWID_FIRMWAREID

命令代码 = 1

查询 =

设置 =

事件 =

设置 InformationBuffer 有效负载 = 不适用

查询 InformationBuffer 有效负载 = 不适用

完成 InformationBuffer 有效负载 = UUID

CID_MBIM_MSFWID_FIRMWAREID

该命令将返回 MNO 或 IHV 为设备分配的固件 ID。 UUID 将根据 MBIM 规范中的准则进行编码。

查询 = 未使用的 MBIM_COMMAND_MSG 上的 InformationBuffer。在 InformationBuffer MBIM_COMMAND_DONE 中返回的 UUID。

设置 = 不受支持

未经请求的事件 = 不受支持

UMDF 驱动程序行为的代码片段

如前所述,UMDF 驱动程序应向 Windows 指明启动和完成固件升级的时间。 本部分提供代码片段,其中显示了驱动程序应如何通知 Windows 这些事件。

/**
 * This is the IPnpCallbackHardware*:OnPrepareHardware handler 
 * in the UMDF driver. This is called every time the firmware 
 * update is device is started. Since this handler should be 
 * blocked from returning actual the firmware update process 
 * should be done in a workitem 
 */
HRESULT
CMyDevice::OnPrepareHardware(IWDFDevice* pDevice)
{
    HRESULT hr = S_OK;
    BOOL bFirmwareUpdateInProgress = FALSE;
    BOOL bFirmwareUpdateNeeded = FALSE;
    BOOL bFirmwareUpdateIsDone = FALSE;

    //
    // The snippets below demonstrates the steps for firmware 
    // update against a MB device that loads the updated firmware 
    // on device boot. So the firmware update driver needs to
    // send the new firmware down to the device and then tell 
    // the device to initiate a stop/start. Once the device has
    // reappeared, it would have automatically loaded the 
    // new firmware
    // 


    //
    // First, determine if firmware update is in progress. This 
    // can be based on some registry key that is saved when
    // firmware update is started
    //

    // Assuming this status is returned in bFirmwareUpdateInProgress
    if (bFirmwareUpdateInProgress)
    {
        //
        // If firmware update is in progress, check if its done. For
        // this it may be necessary to access the MB device. Note that 
        // if the MB device (& hence the Firmware update device) needs
        // multiple stop/starts to do the firmware update. In that case
        // it will be marked as done at the end of the process
        //

        // Assuming this status is returned in bFirmwareUpdateIsDone
        if (bFirmwareUpdateIsDone)
        {
            //
            // Signal the completion of the firmware update
            // process.
            //
            SignalFirmwareUpdateComplete(pDevice);
        }
        else
        {
            //
            // Take appropriate steps to get notified when
            // firmware update is done. Call SignalFirmwareUpdateComplete
            // when that notification is received
            //
        }
    }
    else
    {
        //
        // Determine if firmware update is needed. This can be 
        // based on checking state in the registry of the last
        // firmware version set on the device to the firmware
        // version associated with this driver
        //
        
        // Assuming this status is returned in bFirmwareUpdateNeeded
        if (bFirmwareUpdateNeeded)
        {
            // 
            // Create and queue a workitem to perform the firmware
            // update process. IWDFWorkItem can be used for this
            //
            
            // Assuming the creation/enquing status
            // is returned in hr
            
            if (SUCCEEDED(hr))
            {
                //
                // Work item queued. It will do the firmware update
                // Tell the OS that firmware update is in progress
                //
                SignalFirmwareUpdateInProgress(pDevice);
            }
        }
    }

    //
    // If we have a failure, we clear the firmware update
    // in progress state
    //
    if (FAILED(hr))
    {
        SignalFirmwareUpdateComplete(pDevice);
    }
    return S_OK;
}

/**
 * This function tells the OS that firmware update is in progress.
 * It should be called from the firmware update UMDF driver's 
 * IPnpCallbackHardware*:OnPrepareHardware handler after it has
 * successfully queued a workitem to perform the firmware update
 */
HRESULT
CMyDevice::SignalFirmwareUpdateInProgress(
    __in IWDFDevice* pDevice
    )
{
    HRESULT hr = S_OK;    
    IWDFUnifiedPropertyStoreFactory* spPropertyStoreFactory = NULL;
    IWDFUnifiedPropertyStore* spPropStore = NULL;
    WDF_PROPERTY_STORE_ROOT wdfPropRoot = { sizeof(WDF_PROPERTY_STORE_ROOT), WdfPropertyStoreRootClassHardwareKey };
    DEVPROP_BOOLEAN boolValue = DEVPROP_TRUE;
    
    do
    {
       
        hr = pDevice->QueryInterface(IID_PPV_ARGS(&spPropertyStoreFactory));
        if (FAILED(hr))
        {
            Trace(TRACE_LEVEL_ERROR, "Failed to query for property store factory. Error = 0x%x", hr);
            break;

        }
        
        hr = spPropertyStoreFactory->RetrieveUnifiedDevicePropertyStore(
            &wdfPropRoot,
            &spPropStore
            );
        if (FAILED(hr))
        {
            Trace(TRACE_LEVEL_ERROR, "Failed to query for device property store. Error = 0x%x", hr);
            break;
        }

        // Set the OS flag
        hr = spPropStore->SetPropertyData(
            reinterpret_cast<const DEVPROPKEY*>(&DEVPKEY_Device_PostInstallInProgress),
            0, // this property is language neutral
            0,
            DEVPROP_TYPE_BOOLEAN,
            sizeof(DEVPROP_BOOLEAN),
            &boolValue
            );
        if (FAILED(hr))
        {
            Trace(TRACE_LEVEL_ERROR, "Failed to set device property for PostInstallInProgress. Error = 0x%x", hr);
            break;
        }

        //
        // Save some state so that we know we are in the process
        // of firmware update
        //
    } while (FALSE);        

    if (spPropStore)
    {
        spPropStore->Release();
    }

    if (spPropertyStoreFactory)
    {
        spPropertyStoreFactory->Release();
    }

    return hr;
}


/**
 * This function tells the OS that firmware update is done
 * It should be called only after the full firmware update process
 * (including any MB device stop/start) has finished
 */
HRESULT
CMyDevice::SignalFirmwareUpdateComplete(
    __in IWDFDevice* pDevice
    )
{
    HRESULT hr = S_OK;    
    IWDFUnifiedPropertyStoreFactory* spPropertyStoreFactory = NULL;
    IWDFUnifiedPropertyStore* spPropStore = NULL;
    WDF_PROPERTY_STORE_ROOT wdfPropRoot = { sizeof(WDF_PROPERTY_STORE_ROOT), WdfPropertyStoreRootClassHardwareKey };
    
    do
    {
        hr = pDevice->QueryInterface(IID_PPV_ARGS(&spPropertyStoreFactory));
        if (FAILED(hr))
        {
            Trace(TRACE_LEVEL_ERROR, "Failed to query for property store factory. Error = 0x%x", hr);
            break;

        }

        hr = spPropertyStoreFactory->RetrieveUnifiedDevicePropertyStore(
            &wdfPropRoot,
            &spPropStore
            );
        if (FAILED(hr))
        {
            Trace(TRACE_LEVEL_ERROR, "Failed to query for device property store. Error = 0x%x", hr);
            break;
        }

        hr = spPropStore->SetPropertyData(
            reinterpret_cast<const DEVPROPKEY*>(&DEVPKEY_Device_PostInstallInProgress),
            0, // this property is language neutral
            0,
            DEVPROP_TYPE_BOOLEAN,
            0,
            NULL
            );
        if (FAILED(hr))
        {
            Trace(TRACE_LEVEL_ERROR, "Failed to clear device property for PostInstallInProgress. Error = 0x%x", hr);
            break;
        }

        //
        // Save some state so that we can do quick check on 
        // whether firmware update is needed or not
        //

    } while (FALSE);        

    if (spPropStore)
    {
        spPropStore->Release();
    }

    if (spPropertyStoreFactory)
    {
        spPropertyStoreFactory->Release();
    }

    return hr;
}