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 ATTR_WILL_DETACH = 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, DEFAULT_WINUSB_DEVICE_INTERFACE_GUID,
30 DEFAULT_WINUSB_VENDOR_CODE, WinUsbMsOs20Scope::FUNCTION),
31 jump_to_bootloader_(jump_to_bootloader),
32 jump_ctx_(jump_ctx),
33 default_detach_timeout_ms_(detach_timeout_ms)
34 {
35 }
36
37 // Runtime DFU 只有一个延迟动作:DETACH 超时后跳到板级 bootloader 入口。
38 // Runtime DFU only has one deferred action: jump to the board-specific
39 // bootloader entry after the DETACH timeout expires.
40 void Process()
41 {
42 if (!detach_pending_)
43 {
44 return;
45 }
46
47 const uint32_t now_ms = static_cast<uint32_t>(LibXR::Timebase::GetMilliseconds());
48 if (static_cast<int32_t>(now_ms - detach_deadline_ms_) < 0)
49 {
50 return;
51 }
52
53 detach_pending_ = false;
54 if (jump_to_bootloader_ != nullptr)
55 {
57 }
58 }
59
60 protected:
61LIBXR_PACKED_BEGIN
67 {
68 uint8_t bLength = 9;
69 uint8_t bDescriptorType = 0x21;
70 uint8_t bmAttributes = 0u;
71 uint16_t wDetachTimeOut = 0;
72 uint16_t wTransferSize = 0;
73 uint16_t bcdDFUVersion = 0x0110u;
74 };
75
80 {
81 uint8_t bStatus = static_cast<uint8_t>(DFUStatusCode::OK);
82 uint8_t bwPollTimeout[3] = {0, 0, 0};
83 uint8_t bState = static_cast<uint8_t>(DFUState::APP_IDLE);
84 uint8_t iString = 0;
85
86 void SetPollTimeout(uint32_t timeout_ms)
87 {
88 bwPollTimeout[0] = static_cast<uint8_t>(timeout_ms & 0xFFu);
89 bwPollTimeout[1] = static_cast<uint8_t>((timeout_ms >> 8) & 0xFFu);
90 bwPollTimeout[2] = static_cast<uint8_t>((timeout_ms >> 16) & 0xFFu);
91 }
92 };
93
99 {
101 9, static_cast<uint8_t>(DescriptorType::INTERFACE), 0, 0, 0, 0xFEu, 0x01u, 0x01u,
102 0};
103 FunctionalDescriptor func_desc = {};
104 };
105LIBXR_PACKED_END
106
107 void BindEndpoints(EndpointPool&, uint8_t start_itf_num, bool) override
108 {
109 // Runtime DFU 没有数据端点;绑定阶段只发布接口/功能描述符,并重置 detach 状态机。
110 // Runtime DFU has no data endpoints; bind only publishes the interface /
111 // functional descriptors and resets the detach state machine.
112 interface_num_ = start_itf_num;
113 UpdateWinUsbFunctionInterface(interface_num_);
114 current_alt_setting_ = 0u;
115 detach_pending_ = false;
117 state_ = DFUState::APP_IDLE;
118 desc_block_.interface_desc.bInterfaceNumber = interface_num_;
120 desc_block_.func_desc.bmAttributes =
121 (jump_to_bootloader_ != nullptr) ? ATTR_WILL_DETACH : 0u;
122 desc_block_.func_desc.wDetachTimeOut =
123 (jump_to_bootloader_ != nullptr) ? detach_timeout_ms_ : 0u;
124 desc_block_.func_desc.wTransferSize = 0u;
125 SetData(RawData{reinterpret_cast<uint8_t*>(&desc_block_), sizeof(desc_block_)});
126 inited_ = true;
127 }
128
129 void UnbindEndpoints(EndpointPool&, bool) override
130 {
131 // 解绑阶段只清理 runtime detach 状态;这里不持有额外 backend 资源。
132 // Unbind only clears runtime detach state; no backend-owned resources live here.
133 detach_pending_ = false;
134 state_ = DFUState::APP_IDLE;
135 inited_ = false;
136 }
137
138 size_t GetInterfaceCount() override { return 1u; }
139 bool HasIAD() override { return false; }
140 size_t GetMaxConfigSize() override { return sizeof(desc_block_); }
141
142 ErrorCode SetAltSetting(uint8_t itf, uint8_t alt) override
143 {
144 if (itf != interface_num_)
145 {
147 }
148 if (alt != 0u)
149 {
151 }
152 current_alt_setting_ = alt;
153 return ErrorCode::OK;
154 }
155
156 ErrorCode GetAltSetting(uint8_t itf, uint8_t& alt) override
157 {
158 if (itf != interface_num_)
159 {
161 }
162 alt = current_alt_setting_;
163 return ErrorCode::OK;
164 }
165
166 ErrorCode OnGetDescriptor(bool, uint8_t, uint16_t wValue, uint16_t,
167 ConstRawData& out_data) override
168 {
169 const uint8_t desc_type = static_cast<uint8_t>((wValue >> 8) & 0xFFu);
170 if (desc_type != desc_block_.func_desc.bDescriptorType)
171 {
173 }
174
175 out_data = {reinterpret_cast<const uint8_t*>(&desc_block_.func_desc),
176 sizeof(desc_block_.func_desc)};
177 return ErrorCode::OK;
178 }
179
180 ErrorCode OnClassRequest(bool, uint8_t bRequest, uint16_t wValue, uint16_t wLength,
181 uint16_t wIndex, ControlTransferResult& result) override
182 {
183 if (!inited_)
184 {
185 return ErrorCode::INIT_ERR;
186 }
187 if ((wIndex & 0xFFu) != interface_num_)
188 {
190 }
191
192 switch (static_cast<DFURequest>(bRequest))
193 {
194 case DFURequest::DETACH:
195 // Runtime DFU 只在 APP_IDLE 接受 DETACH;真正跳转由 Process() 在
196 // 广播给主机的超时到期后再执行。
197 // Runtime DFU only accepts DETACH from APP_IDLE; the actual jump is
198 // deferred to Process() after the advertised timeout expires.
199 if (jump_to_bootloader_ == nullptr)
200 {
202 }
203 if (wLength != 0u || state_ != DFUState::APP_IDLE)
204 {
205 return ErrorCode::ARG_ERR;
206 }
207 detach_timeout_ms_ = (wValue == 0u) ? default_detach_timeout_ms_ : wValue;
208 detach_deadline_ms_ = static_cast<uint32_t>(LibXR::Timebase::GetMilliseconds()) +
210 detach_pending_ = true;
211 state_ = DFUState::APP_DETACH;
212 desc_block_.func_desc.wDetachTimeOut = detach_timeout_ms_;
213 result.SendStatusInZLP() = true;
214 return ErrorCode::OK;
215
216 case DFURequest::GETSTATUS:
217 // GETSTATUS 是主机读取剩余 detach 超时的唯一入口。
218 // GETSTATUS is the only point where the host observes the remaining
219 // detach timeout.
220 status_response_.bStatus = static_cast<uint8_t>(DFUStatusCode::OK);
221 status_response_.bState = static_cast<uint8_t>(state_);
222 if (detach_pending_)
223 {
224 const uint32_t now_ms =
225 static_cast<uint32_t>(LibXR::Timebase::GetMilliseconds());
226 const uint32_t remain_ms =
227 (static_cast<int32_t>(detach_deadline_ms_ - now_ms) > 0)
228 ? (detach_deadline_ms_ - now_ms)
229 : 0u;
230 status_response_.SetPollTimeout(remain_ms);
231 }
232 else
233 {
234 status_response_.SetPollTimeout(0u);
235 }
236 result.InData() = {reinterpret_cast<const uint8_t*>(&status_response_),
237 sizeof(status_response_)};
238 return ErrorCode::OK;
239
240 case DFURequest::GETSTATE:
241 state_response_ = static_cast<uint8_t>(state_);
242 result.InData() = {&state_response_, sizeof(state_response_)};
243 return ErrorCode::OK;
244
245 default:
246 return ErrorCode::ARG_ERR;
247 }
248 }
249
250 private:
253 JumpCallback jump_to_bootloader_ = nullptr;
254 void* jump_ctx_ = nullptr;
255 uint8_t state_response_ = 0u;
256 bool detach_pending_ = false;
258 50u;
259 uint16_t detach_timeout_ms_ = 50u;
260 uint32_t detach_deadline_ms_ = 0u;
261 DFUState state_ = DFUState::APP_IDLE;
262};
263
264} // namespace LibXR::USB
只读原始数据视图 / Immutable raw data view
可写原始数据视图 / Mutable raw data view
static MillisecondTimestamp GetMilliseconds()
获取当前时间的毫秒级时间戳。 Gets the current timestamp in milliseconds.
Definition timebase.hpp:62
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:87
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
uint16_t default_detach_timeout_ms_
默认 detach 超时 / Default detach timeout
LIBXR_PACKED_END void BindEndpoints(EndpointPool &, uint8_t start_itf_num, bool) override
绑定端点资源 / Bind endpoint resources
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.