libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
dfu_runtime.hpp
1#pragma once
2
3#include "dfu/dfu_def.hpp"
4
5namespace LibXR::USB
6{
12{
13 public:
14 using JumpCallback = void (*)(void*);
15 static constexpr const char* DEFAULT_INTERFACE_STRING = "XRUSB DFU RT";
16 static constexpr uint8_t kAttrWillDetach = 0x08u;
17
23 JumpCallback jump_to_bootloader, void* jump_ctx = nullptr,
24 uint16_t detach_timeout_ms = 50u,
25 const char* interface_string = DEFAULT_INTERFACE_STRING,
26 const char* webusb_landing_page_url = nullptr,
27 uint8_t webusb_vendor_code = LibXR::USB::WebUsb::WEBUSB_VENDOR_CODE_DEFAULT)
28 : DfuInterfaceClassBase(interface_string, webusb_landing_page_url,
29 webusb_vendor_code),
30 jump_to_bootloader_(jump_to_bootloader),
31 jump_ctx_(jump_ctx),
32 default_detach_timeout_ms_(detach_timeout_ms)
33 {
34 }
35
36 // Runtime DFU 只有一个延迟动作:DETACH 超时后跳到板级 bootloader 入口。
37 // Runtime DFU only has one deferred action: jump to the board-specific
38 // bootloader entry after the DETACH timeout expires.
39 void Process()
40 {
41 if (!detach_pending_)
42 {
43 return;
44 }
45
46 const uint32_t now_ms = static_cast<uint32_t>(LibXR::Timebase::GetMilliseconds());
47 if (static_cast<int32_t>(now_ms - detach_deadline_ms_) < 0)
48 {
49 return;
50 }
51
52 detach_pending_ = false;
53 if (jump_to_bootloader_ != nullptr)
54 {
56 }
57 }
58
59 protected:
60#pragma pack(push, 1)
66 {
67 uint8_t bLength = 9;
68 uint8_t bDescriptorType = 0x21;
69 uint8_t bmAttributes = 0u;
70 uint16_t wDetachTimeOut = 0;
71 uint16_t wTransferSize = 0;
72 uint16_t bcdDFUVersion = 0x0110u;
73 };
74
79 {
80 uint8_t bStatus = static_cast<uint8_t>(DFUStatusCode::OK);
81 uint8_t bwPollTimeout[3] = {0, 0, 0};
82 uint8_t bState = static_cast<uint8_t>(DFUState::APP_IDLE);
83 uint8_t iString = 0;
84
85 void SetPollTimeout(uint32_t timeout_ms)
86 {
87 bwPollTimeout[0] = static_cast<uint8_t>(timeout_ms & 0xFFu);
88 bwPollTimeout[1] = static_cast<uint8_t>((timeout_ms >> 8) & 0xFFu);
89 bwPollTimeout[2] = static_cast<uint8_t>((timeout_ms >> 16) & 0xFFu);
90 }
91 };
92
98 {
100 9, static_cast<uint8_t>(DescriptorType::INTERFACE), 0, 0, 0, 0xFEu, 0x01u, 0x01u,
101 0};
102 FunctionalDescriptor func_desc = {};
103 };
104#pragma pack(pop)
105
106 void BindEndpoints(EndpointPool&, uint8_t start_itf_num, bool) override
107 {
108 // Runtime DFU 没有数据端点;绑定阶段只发布接口/功能描述符,并重置 detach 状态机。
109 // Runtime DFU has no data endpoints; bind only publishes the interface /
110 // functional descriptors and resets the detach state machine.
111 interface_num_ = start_itf_num;
112 current_alt_setting_ = 0u;
113 detach_pending_ = false;
115 state_ = DFUState::APP_IDLE;
116 desc_block_.interface_desc.bInterfaceNumber = interface_num_;
118 desc_block_.func_desc.bmAttributes =
119 (jump_to_bootloader_ != nullptr) ? kAttrWillDetach : 0u;
120 desc_block_.func_desc.wDetachTimeOut =
121 (jump_to_bootloader_ != nullptr) ? detach_timeout_ms_ : 0u;
122 desc_block_.func_desc.wTransferSize = 0u;
123 SetData(RawData{reinterpret_cast<uint8_t*>(&desc_block_), sizeof(desc_block_)});
124 inited_ = true;
125 }
126
127 void UnbindEndpoints(EndpointPool&, bool) override
128 {
129 // 解绑阶段只清理 runtime detach 状态;这里不持有额外 backend 资源。
130 // Unbind only clears runtime detach state; no backend-owned resources live here.
131 detach_pending_ = false;
132 state_ = DFUState::APP_IDLE;
133 inited_ = false;
134 }
135
136 size_t GetInterfaceCount() override { return 1u; }
137 bool HasIAD() override { return false; }
138 size_t GetMaxConfigSize() override { return sizeof(desc_block_); }
139
140 ErrorCode SetAltSetting(uint8_t itf, uint8_t alt) override
141 {
142 if (itf != interface_num_)
143 {
145 }
146 if (alt != 0u)
147 {
149 }
150 current_alt_setting_ = alt;
151 return ErrorCode::OK;
152 }
153
154 ErrorCode GetAltSetting(uint8_t itf, uint8_t& alt) override
155 {
156 if (itf != interface_num_)
157 {
159 }
160 alt = current_alt_setting_;
161 return ErrorCode::OK;
162 }
163
164 ErrorCode OnGetDescriptor(bool, uint8_t, uint16_t wValue, uint16_t,
165 ConstRawData& out_data) override
166 {
167 const uint8_t desc_type = static_cast<uint8_t>((wValue >> 8) & 0xFFu);
168 if (desc_type != desc_block_.func_desc.bDescriptorType)
169 {
171 }
172
173 out_data = {reinterpret_cast<const uint8_t*>(&desc_block_.func_desc),
174 sizeof(desc_block_.func_desc)};
175 return ErrorCode::OK;
176 }
177
178 ErrorCode OnClassRequest(bool, uint8_t bRequest, uint16_t wValue, uint16_t wLength,
179 uint16_t wIndex, ControlTransferResult& result) override
180 {
181 if (!inited_)
182 {
183 return ErrorCode::INIT_ERR;
184 }
185 if ((wIndex & 0xFFu) != interface_num_)
186 {
188 }
189
190 switch (static_cast<DFURequest>(bRequest))
191 {
192 case DFURequest::DETACH:
193 // Runtime DFU 只在 APP_IDLE 接受 DETACH;真正跳转由 Process() 在
194 // 广播给主机的超时到期后再执行。
195 // Runtime DFU only accepts DETACH from APP_IDLE; the actual jump is
196 // deferred to Process() after the advertised timeout expires.
197 if (jump_to_bootloader_ == nullptr)
198 {
200 }
201 if (wLength != 0u || state_ != DFUState::APP_IDLE)
202 {
203 return ErrorCode::ARG_ERR;
204 }
205 detach_timeout_ms_ = (wValue == 0u) ? default_detach_timeout_ms_ : wValue;
206 detach_deadline_ms_ = static_cast<uint32_t>(LibXR::Timebase::GetMilliseconds()) +
208 detach_pending_ = true;
209 state_ = DFUState::APP_DETACH;
210 desc_block_.func_desc.wDetachTimeOut = detach_timeout_ms_;
211 result.SendStatusInZLP() = true;
212 return ErrorCode::OK;
213
214 case DFURequest::GETSTATUS:
215 // GETSTATUS 是主机读取剩余 detach 超时的唯一入口。
216 // GETSTATUS is the only point where the host observes the remaining
217 // detach timeout.
218 status_response_.bStatus = static_cast<uint8_t>(DFUStatusCode::OK);
219 status_response_.bState = static_cast<uint8_t>(state_);
220 if (detach_pending_)
221 {
222 const uint32_t now_ms =
223 static_cast<uint32_t>(LibXR::Timebase::GetMilliseconds());
224 const uint32_t remain_ms =
225 (static_cast<int32_t>(detach_deadline_ms_ - now_ms) > 0)
226 ? (detach_deadline_ms_ - now_ms)
227 : 0u;
228 status_response_.SetPollTimeout(remain_ms);
229 }
230 else
231 {
232 status_response_.SetPollTimeout(0u);
233 }
234 result.InData() = {reinterpret_cast<const uint8_t*>(&status_response_),
235 sizeof(status_response_)};
236 return ErrorCode::OK;
237
238 case DFURequest::GETSTATE:
239 state_response_ = static_cast<uint8_t>(state_);
240 result.InData() = {&state_response_, sizeof(state_response_)};
241 return ErrorCode::OK;
242
243 default:
244 return ErrorCode::ARG_ERR;
245 }
246 }
247
248 private:
251 JumpCallback jump_to_bootloader_ = nullptr;
252 void* jump_ctx_ = nullptr;
253 uint8_t state_response_ = 0u;
254 bool detach_pending_ = false;
256 50u;
257 uint16_t detach_timeout_ms_ = 50u;
258 uint32_t detach_deadline_ms_ = 0u;
259 DFUState state_ = DFUState::APP_IDLE;
260};
261
262} // namespace LibXR::USB
常量原始数据封装类。 A class for encapsulating constant raw data.
原始数据封装类。 A class for encapsulating raw data.
static MillisecondTimestamp GetMilliseconds()
获取当前时间的毫秒级时间戳。 Gets the current timestamp in milliseconds.
Definition timebase.hpp:63
void SetData(RawData data)
设置内部数据缓存 / Set internal data cache
Definition desc_cfg.hpp:193
uint8_t GetInterfaceStringIndex(size_t local_interface_index) const
返回已分配的接口字符串索引 Return the assigned USB string index for a local interface.
DFU 单接口类公共基类 / Common base for single-interface DFU classes.
Definition dfu_def.hpp:86
Runtime DFU 类:只负责 DETACH 后跳转 bootloader Runtime DFU class: only handles DETACH and later jumps to boo...
ErrorCode SetAltSetting(uint8_t itf, uint8_t alt) override
可选:设置接口备用设置 / Optional: set interface alternate setting
void BindEndpoints(EndpointPool &, uint8_t start_itf_num, bool) override
绑定端点资源 / Bind endpoint resources
uint16_t default_detach_timeout_ms_
默认 detach 超时 / Default detach timeout
DfuRuntimeClass(JumpCallback jump_to_bootloader, void *jump_ctx=nullptr, uint16_t detach_timeout_ms=50u, const char *interface_string=DEFAULT_INTERFACE_STRING, const char *webusb_landing_page_url=nullptr, uint8_t webusb_vendor_code=LibXR::USB::WebUsb::WEBUSB_VENDOR_CODE_DEFAULT)
构造 Runtime DFU 类 Construct the runtime DFU class.
uint32_t detach_deadline_ms_
detach 截止时刻 / Detach deadline tick
size_t GetMaxConfigSize() override
最大配置描述符占用 / Maximum bytes required in configuration descriptor
uint8_t state_response_
GETSTATE 缓冲字节 / GETSTATE byte buffer.
bool detach_pending_
是否等待 detach 超时 / Waiting for detach timeout
void UnbindEndpoints(EndpointPool &, bool) override
解绑端点资源 / Unbind endpoint resources
size_t GetInterfaceCount() override
接口数量 / Number of interfaces contributed
DFUState state_
Runtime DFU 状态 / Runtime DFU state.
JumpCallback jump_to_bootloader_
跳 boot 回调 / Boot jump callback
ErrorCode OnClassRequest(bool, uint8_t bRequest, uint16_t wValue, uint16_t wLength, uint16_t wIndex, ControlTransferResult &result) override
处理 Class-specific 请求(Setup stage)/ Handle class-specific request (Setup stage)
void * jump_ctx_
跳转上下文 / Jump callback context
ErrorCode OnGetDescriptor(bool, uint8_t, uint16_t wValue, uint16_t, ConstRawData &out_data) override
处理标准请求 GET_DESCRIPTOR(类特定描述符) Handle standard GET_DESCRIPTOR request (class-specific descriptors).
uint16_t detach_timeout_ms_
当前 detach 超时 / Active detach timeout
ErrorCode GetAltSetting(uint8_t itf, uint8_t &alt) override
可选:获取接口备用设置 / Optional: get interface alternate setting
DescriptorBlock desc_block_
描述符缓存 / Descriptor cache
bool HasIAD() override
是否包含 IAD / Whether an IAD is used
StatusResponse status_response_
GETSTATUS 缓冲区 / GETSTATUS buffer.
USB端点池类 / USB endpoint pool class.
Definition ep_pool.hpp:23
ErrorCode
定义错误码枚举
@ INIT_ERR
初始化错误 | Initialization error
@ NOT_FOUND
未找到 | Not found
@ NOT_SUPPORT
不支持 | Not supported
@ OK
操作成功 | Operation successful
@ ARG_ERR
参数错误 | Argument error
接口描述符(9 字节)/ Interface descriptor (9 bytes)
Definition desc_cfg.hpp:75
uint8_t iInterface
接口字符串索引 / Interface string index
Definition desc_cfg.hpp:84
uint8_t bInterfaceNumber
接口号 / Interface number
Definition desc_cfg.hpp:78
控制请求(Class/Vendor)处理结果 / Control request (Class/Vendor) handling result
Runtime DFU 的接口描述符块 Runtime DFU descriptor block.
DFU Functional Descriptor(Runtime 变体) DFU Functional Descriptor for the runtime variant.
GETSTATUS 返回包 / GETSTATUS response payload.