跳到主要内容

DFU Bootloader 设备协议栈

本文档描述 XRUSB 的 bootloader DFU 实现:

  • 前端设备类:LibXR::USB::DfuBootloaderClass
  • 存储与状态机后端:LibXR::USB::DfuBootloaderBackend

这条路径用于设备已经进入 bootloader 后的 DNLOAD / UPLOAD / manifest 流程。


1. 类与构造方式

1.1 LibXR::USB::DfuBootloaderBackend

DfuBootloaderBackend 围绕 LibXR::Flash 抽象实现 bootloader 侧的存储与状态机。

构造函数:

LibXR::USB::DfuBootloaderBackend backend(
flash,
image_base,
image_limit,
seal_offset,
jump_to_app,
jump_ctx,
true);

参数说明:

  • flash:底层 Flash 实例
  • image_base:镜像区起始地址
  • image_limit:镜像区总长度
  • seal_offset:镜像区内 seal 记录偏移
  • jump_to_app:跳转到应用固件的回调
  • jump_app_ctx:跳转上下文
  • autorun:manifest 完成后是否允许自动进入应用

1.2 LibXR::USB::DfuBootloaderClass

对外使用时通常直接构造 DfuBootloaderClass,它是 DfuBootloaderClassT<4096> 的别名。

LibXR::USB::DfuBootloaderClass dfu_bl(
flash,
image_base,
image_limit,
seal_offset,
jump_to_app,
jump_ctx,
true,
"XRUSB DFU");

该类内部组合了:

  • DfuBootloaderBackend
  • DFUClass<Backend, MAX_TRANSFER_SIZE>

2. 接口与描述符

DfuBootloaderClass 只贡献 1 个 DFU 接口,不使用 IAD,也不申请额外数据端点,所有数据都经控制传输完成。

  • GetInterfaceCount() = 1
  • HasIAD() = false
  • bInterfaceClass = 0xFE
  • bInterfaceSubClass = 0x01
  • bInterfaceProtocol = 0x02

DFU Functional Descriptor 的关键字段来自 backend 上报的 DFUCapabilities

  • can_download
  • can_upload
  • manifestation_tolerant
  • will_detach
  • detach_timeout_ms
  • transfer_size

当前默认别名 DfuBootloaderClass 的最大传输块尺寸是 4096 字节,写入描述符时会同步到 wTransferSize


3. 支持的请求与行为

当前主线支持:

请求行为
DNLOAD下载固件块,零长度 DNLOAD 进入 manifest
UPLOAD从镜像区按块回读
GETSTATUS返回当前 DFU 状态、错误码与 poll timeout
GETSTATE返回当前状态机状态
ABORT终止当前传输会话
CLRSTATUS清除错误态

补充:

  • bootloader 态 DETACH 不实现,会按协议错误处理
  • 额外支持 vendor request 0x5A,用于请求运行已验证镜像

4. Backend 负责的内容

DfuBootloaderBackend 负责:

  • download / upload / manifest 会话状态
  • 按擦除块管理 Flash 擦除
  • 按块缓存并延迟执行写入
  • 计算镜像 CRC32
  • 写入 seal 记录
  • 维护镜像是否有效、是否可启动

seal 记录格式为:

struct SealRecord
{
uint32_t magic;
uint32_t image_size;
uint32_t crc32;
uint32_t crc32_inv;
};

其中 magic 固定为 "SEAL" 对应的 0x4C414553


5. 运行方式

Bootloader DFU 需要外部周期调用 Process()

dfu_bl.Process();

Process() 负责推进 backend 的延迟动作,包括:

  • 处理待提交写入
  • 处理 manifest

因此这条路径同样不要求专门后台线程,但必须在主循环或周期任务中持续调用 Process()。如果完全不调用,主机看到的 GETSTATUS 会进入等待状态,但实际写入和 manifest 不会继续推进。

若启用了 autorun,镜像 ready 后仍需要上层显式消费启动请求:

if (dfu_bl.TryConsumeAppLaunch(now_ms))
{
}

或者通过 RequestRunApp() / vendor request 0x5A 触发运行应用。


6. 使用示例

#include "dfu/dfu_bootloader.hpp"

static void JumpToApp(void*)
{
BoardJumpToApplication();
}

LibXR::USB::DfuBootloaderClass dfu_bl(
flash,
APP_IMAGE_BASE,
APP_IMAGE_LIMIT,
APP_SEAL_OFFSET,
JumpToApp,
nullptr,
true);

// USB class list: {{&dfu_bl}}
// usb_dev.Init();
// usb_dev.Start();

for (;;)
{
dfu_bl.Process();
dfu_bl.TryConsumeAppLaunch(LibXR::Timebase::GetMilliseconds());
}

7. 与 Runtime DFU 的关系

  • Runtime DFU:应用态响应 DETACH 并跳入 bootloader
  • DFU Bootloader:bootloader 态负责镜像传输、seal、manifest 和运行应用