libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
dfu_bootloader.hpp
1#pragma once
2
3#include <cstring>
4
5#include "dfu/dfu_def.hpp"
6
7namespace LibXR::USB
8{
14{
15 public:
16 using JumpCallback = void (*)(void*);
17 static constexpr uint32_t SEAL_MAGIC = 0x4C414553u; // "SEAL"
18
19 LIBXR_PACKED_BEGIN
24 {
25 uint32_t magic = SEAL_MAGIC;
26 uint32_t image_size = 0u;
27 uint32_t crc32 = 0u;
28 uint32_t crc32_inv = 0u;
29 };
30 LIBXR_PACKED_END
31
32 // Backend 负责:
33 // - 管理 download/upload/manifest 状态
34 // - 通过 Flash 基类接口驱动擦写读
35 // - 维护镜像有效性与 seal 元数据
36 // Backend responsibilities:
37 // - own download/upload/manifest bookkeeping
38 // - drive flash erase/write/read through the Flash base interface
39 // - maintain image validity and seal metadata
40 DfuBootloaderBackend(Flash& flash, size_t image_offset, size_t image_size_limit,
41 size_t seal_offset, JumpCallback jump_to_app,
42 void* jump_app_ctx = nullptr, bool autorun = true)
43 : flash_(flash),
44 image_offset_(image_offset),
45 image_size_limit_(image_size_limit),
46 seal_offset_(seal_offset),
47 jump_to_app_(jump_to_app),
48 jump_app_ctx_(jump_app_ctx),
49 autorun_(autorun)
50 {
51 erase_block_size_ = flash_.MinEraseSize();
52 if (erase_block_size_ == 0u)
53 {
54 erase_block_size_ = 1u;
55 }
56 erase_block_count_ = (image_size_limit_ + erase_block_size_ - 1u) / erase_block_size_;
57 if (erase_block_count_ > 0u)
58 {
59 erased_blocks_ = new uint8_t[erase_block_count_];
60 std::memset(erased_blocks_, 0, erase_block_count_);
61 }
62 seal_storage_size_ =
63 AlignUp(sizeof(SealRecord), NonZeroWriteSize(flash_.MinWriteSize()));
64 seal_storage_ = new uint8_t[seal_storage_size_];
65 transfer_size_ = PayloadLimit();
66 if (transfer_size_ == 0u)
67 {
68 transfer_size_ = 1024u;
69 }
70 if (transfer_size_ > 4096u)
71 {
72 transfer_size_ = 4096u;
73 }
74 write_buffer_ = new uint8_t[transfer_size_];
75 ResetTransferState();
76 image_.launch_requested = false;
77 image_.ready = false;
78 image_.stored_size = 0u;
79 }
80
81 DfuBootloaderBackend(const DfuBootloaderBackend&) = delete;
82 DfuBootloaderBackend& operator=(const DfuBootloaderBackend&) = delete;
83
88 {
89 DFUCapabilities caps = {};
90 caps.can_download = true;
91 caps.can_upload = true;
92 caps.manifestation_tolerant = false;
93 caps.will_detach = false;
94 caps.detach_timeout_ms = 0u;
95
96 if (transfer_size_ > 0xFFFFu)
97 {
98 caps.transfer_size = 0xFFFFu;
99 }
100 else
101 {
102 caps.transfer_size = static_cast<uint16_t>(transfer_size_);
103 }
104 return caps;
105 }
106
107 ErrorCode DfuSetAlternate(uint8_t alt)
108 {
109 return (alt == 0u) ? ErrorCode::OK : ErrorCode::NOT_SUPPORT;
110 }
111
115 void DfuAbort(uint8_t)
116 {
117 ResetTransferState();
118 image_.launch_requested = false;
119 image_.ready = false;
120 image_.stored_size = 0u;
121 }
122
126 void DfuClearStatus(uint8_t)
127 {
128 ResetTransferState();
129 image_.launch_requested = false;
130 }
131
132 DFUStatusCode DfuDownload(uint8_t alt, uint16_t block_num, ConstRawData data,
133 uint32_t& poll_timeout_ms)
134 {
135 poll_timeout_ms = 0u;
136
137 if (alt != 0u)
138 {
139 return DFUStatusCode::ERR_TARGET;
140 }
141 if (data.addr_ == nullptr || data.size_ == 0u || data.size_ > transfer_size_)
142 {
143 return DFUStatusCode::ERR_USBR;
144 }
145 if (HasPendingWrite() || HasPendingManifest())
146 {
147 return DFUStatusCode::ERR_NOTDONE;
148 }
149
150 if ((block_num == 0u) &&
151 (download_.session_started &&
152 (download_.received_bytes != 0u || download_.expected_block_num != 0u)))
153 {
154 // 主机没有先发 ABORT/CLRSTATUS,就直接从 block 0 重启 DNLOAD;
155 // 这里把它视为新会话,并丢弃旧的部分传输状态。
156 // The host restarted DNLOAD from block 0 without an explicit
157 // ABORT/CLRSTATUS first; treat that as a fresh session and discard the
158 // old partial transfer state.
159 ResetTransferState();
160 }
161
162 if (!download_.session_started)
163 {
164 if (block_num != 0u)
165 {
166 return DFUStatusCode::ERR_ADDRESS;
167 }
168 StartDownloadSession();
169 }
170 else if (block_num != download_.expected_block_num)
171 {
172 return DFUStatusCode::ERR_ADDRESS;
173 }
174
175 const size_t offset = download_.received_bytes;
176 const size_t payload_limit = PayloadLimit();
177 if (offset + data.size_ < offset || (offset + data.size_) > payload_limit)
178 {
179 return DFUStatusCode::ERR_ADDRESS;
180 }
181
182 std::memcpy(write_buffer_, data.addr_, data.size_);
183 write_.offset = offset;
184 write_.len = data.size_;
185 write_.block_num = block_num;
186 write_.pending = true;
187 download_.last_status = DFUStatusCode::OK;
188 download_.next_poll_timeout_ms = ComputeWritePollTimeout(offset, data.size_);
189 poll_timeout_ms = download_.next_poll_timeout_ms;
190 return DFUStatusCode::OK;
191 }
192
193 DFUStatusCode DfuGetDownloadStatus(uint8_t alt, bool& busy, uint32_t& poll_timeout_ms)
194 {
195 if (alt != 0u)
196 {
197 busy = false;
198 poll_timeout_ms = 0u;
199 return DFUStatusCode::ERR_TARGET;
200 }
201 busy = HasPendingWrite();
202 poll_timeout_ms = busy ? download_.next_poll_timeout_ms : 0u;
203 return download_.last_status;
204 }
205
206 size_t DfuUpload(uint8_t alt, uint16_t block_num, RawData data, DFUStatusCode& status,
207 uint32_t& poll_timeout_ms)
208 {
209 poll_timeout_ms = 0u;
210 status = DFUStatusCode::OK;
211
212 if (alt != 0u)
213 {
214 status = DFUStatusCode::ERR_TARGET;
215 return 0u;
216 }
217 if (data.addr_ == nullptr || data.size_ == 0u)
218 {
219 status = DFUStatusCode::ERR_USBR;
220 return 0u;
221 }
222
223 if (block_num == 0u)
224 {
225 upload_.session_started = true;
226 upload_.offset = 0u;
227 upload_.expected_block_num = 1u;
228 size_t image_size = 0u;
229 if (image_.ready)
230 {
231 image_size = image_.stored_size;
232 }
233 else if (!ProbeStoredImage(&image_size))
234 {
235 image_size = 0u;
236 }
237 upload_.image_size = image_size;
238 if (upload_.image_size == 0u)
239 {
240 status = DFUStatusCode::ERR_FIRMWARE;
241 return 0u;
242 }
243 }
244 else if (!upload_.session_started || block_num != upload_.expected_block_num)
245 {
246 status = DFUStatusCode::ERR_ADDRESS;
247 return 0u;
248 }
249
250 if (upload_.offset >= upload_.image_size)
251 {
252 return 0u;
253 }
254
255 size_t read_size = upload_.image_size - upload_.offset;
256 if (read_size > data.size_)
257 {
258 read_size = data.size_;
259 }
260 if (flash_.Read(image_offset_ + upload_.offset,
261 {reinterpret_cast<uint8_t*>(data.addr_), read_size}) != ErrorCode::OK)
262 {
263 status = DFUStatusCode::ERR_FIRMWARE;
264 return 0u;
265 }
266
267 upload_.offset += read_size;
268 upload_.expected_block_num = static_cast<uint16_t>(block_num + 1u);
269 return read_size;
270 }
271
275 DFUStatusCode DfuManifest(uint8_t alt, uint32_t& poll_timeout_ms)
276 {
277 poll_timeout_ms = 0u;
278
279 if (alt != 0u)
280 {
281 return DFUStatusCode::ERR_TARGET;
282 }
283 if (!download_.session_started || download_.received_bytes == 0u || HasPendingWrite())
284 {
285 return DFUStatusCode::ERR_NOTDONE;
286 }
287 manifest_.pending = true;
288 manifest_.last_status = DFUStatusCode::OK;
289 poll_timeout_ms = manifest_.poll_timeout_ms;
290 return DFUStatusCode::OK;
291 }
292
296 DFUStatusCode DfuGetManifestStatus(uint8_t alt, bool& busy, uint32_t& poll_timeout_ms)
297 {
298 if (alt != 0u)
299 {
300 busy = false;
301 poll_timeout_ms = 0u;
302 return DFUStatusCode::ERR_TARGET;
303 }
304 busy = HasPendingManifest();
305 poll_timeout_ms = busy ? manifest_.poll_timeout_ms : 0u;
306 return manifest_.last_status;
307 }
308
309 void Process()
310 {
311 // 这里仅推进协议自身拥有的异步工作;
312 // app launch 仍然保持为显式的板级/应用层策略决策。
313 // Only protocol-owned asynchronous work advances here;
314 // app launch remains an explicit board/application policy decision.
315 if (write_.pending)
316 {
317 ProcessPendingWrite();
318 return;
319 }
320 if (manifest_.pending)
321 {
322 ProcessPendingManifest();
323 }
324 }
325
326 bool TryRequestRunApp()
327 {
328 // Run-app 请求不修改 DFU 传输状态;它只记录一个待消费的镜像启动请求,
329 // 由外层 boot/application 循环决定何时真正跳转。
330 // The run-app request does not mutate DFU transfer state; it only records
331 // an image-backed launch request for the outer boot/application loop.
332 if (!image_.ready)
333 {
334 size_t image_size = 0u;
335 image_.ready = ProbeStoredImage(&image_size);
336 image_.stored_size = image_.ready ? image_size : 0u;
337 }
338 if (image_.ready)
339 {
340 image_.launch_requested = true;
341 return true;
342 }
343 return false;
344 }
345
346 void DfuCommitManifestWaitReset(uint8_t alt)
347 {
348 if (alt != 0u)
349 {
350 return;
351 }
352 if (autorun_ && image_.ready)
353 {
354 image_.launch_requested = true;
355 }
356 }
357
358 bool TryConsumeAppLaunch(uint32_t)
359 {
360 if (!image_.launch_requested || !image_.ready)
361 {
362 return false;
363 }
364 image_.launch_requested = false;
365 if (jump_to_app_ != nullptr)
366 {
367 jump_to_app_(jump_app_ctx_);
368 }
369 return true;
370 }
371
372 bool HasPendingWork() const { return HasPendingWrite() || HasPendingManifest(); }
373
374 bool HasValidImage() const { return image_.ready; }
375 size_t ImageSize() const { return image_.stored_size; }
376
377 private:
378 // download/upload 只允许访问 seal 记录之前的 payload 区域。
379 // Download/upload only operate on the payload area before the seal record.
380 size_t PayloadLimit() const
381 {
382 if (seal_offset_ > image_size_limit_)
383 {
384 return 0u;
385 }
386 return seal_offset_;
387 }
388
389 static size_t NonZeroWriteSize(size_t write_size)
390 {
391 return (write_size == 0u) ? 1u : write_size;
392 }
393
394 static size_t AlignUp(size_t value, size_t align)
395 {
396 if (align <= 1u)
397 {
398 return value;
399 }
400 const size_t rem = value % align;
401 return (rem == 0u) ? value : (value + align - rem);
402 }
403
404 // 开启新的下载会话,并使之前缓存的镜像有效性视图失效。
405 // Start a fresh download session and invalidate any previously cached image view.
406 void StartDownloadSession()
407 {
408 ResetTransferState();
409 if (erase_block_count_ > 0u)
410 {
411 std::memset(erased_blocks_, 0, erase_block_count_);
412 }
413 download_.session_started = true;
414 image_.ready = false;
415 image_.stored_size = 0u;
416 image_.launch_requested = false;
417 }
418
419 void ResetTransferState()
420 {
421 // 这里只重置协议拥有的传输会话状态;
422 // 镜像有效性和启动策略单独放在 ImageState 里。
423 // Reset only protocol-owned transfer session state;
424 // image validity and launch policy are tracked separately in ImageState.
425 download_ = {};
426 write_ = {};
427 manifest_ = {};
428 upload_ = {};
429 }
430
431 bool HasPendingWrite() const { return write_.pending; }
432
433 bool HasPendingManifest() const { return manifest_.pending; }
434
435 // 为下一次 GETSTATUS 返回一个粗粒度的 host-visible poll timeout;
436 // 仍需擦除的块会比纯写入步骤报告更长的等待时间。
437 // Return a coarse host-visible poll timeout for the next GETSTATUS;
438 // blocks that still need erase are reported slower than pure program-only writes.
439 uint32_t ComputeWritePollTimeout(size_t offset, size_t len) const
440 {
441 const size_t first_block = offset / erase_block_size_;
442 const size_t last_block = (offset + len - 1u) / erase_block_size_;
443 for (size_t block = first_block; block <= last_block && block < erase_block_count_;
444 ++block)
445 {
446 if (erased_blocks_[block] == 0u)
447 {
448 return 25u;
449 }
450 }
451 return 10u;
452 }
453
454 // 执行一次延迟写入步骤:
455 // 1) 对新覆盖到的擦除块各擦一次
456 // 2) 再把刚收到的 payload 分片编程进去
457 // Execute one deferred write step:
458 // 1) erase each newly touched erase block once
459 // 2) program the just-received payload chunk
460 void ProcessPendingWrite()
461 {
462 if (!EnsureBlocksErased(write_.offset, write_.len))
463 {
464 download_.last_status = DFUStatusCode::ERR_ERASE;
465 write_.pending = false;
466 return;
467 }
468 if (flash_.Write(image_offset_ + write_.offset, {write_buffer_, write_.len}) !=
470 {
471 download_.last_status = DFUStatusCode::ERR_PROG;
472 write_.pending = false;
473 return;
474 }
475
476 download_.received_bytes = write_.offset + write_.len;
477 download_.expected_block_num = static_cast<uint16_t>(write_.block_num + 1u);
478 download_.last_status = DFUStatusCode::OK;
479 write_.pending = false;
480 }
481
482 // 通过计算固定 CRC32 并写入 seal 记录来完成镜像定稿。
483 // Finalize the image by computing the fixed CRC32 and writing the seal record.
484 void ProcessPendingManifest()
485 {
486 const size_t payload_limit = PayloadLimit();
487 if (download_.received_bytes == 0u || download_.received_bytes > payload_limit)
488 {
489 manifest_.last_status = DFUStatusCode::ERR_ADDRESS;
490 manifest_.pending = false;
491 return;
492 }
493
494 uint32_t crc32 = 0u;
495 if (!ComputeImageCrc32(download_.received_bytes, crc32))
496 {
497 manifest_.last_status = DFUStatusCode::ERR_VERIFY;
498 manifest_.pending = false;
499 return;
500 }
501 if (!WriteSeal(download_.received_bytes, crc32))
502 {
503 manifest_.last_status = DFUStatusCode::ERR_VERIFY;
504 manifest_.pending = false;
505 return;
506 }
507
508 image_.stored_size = download_.received_bytes;
509 image_.ready = true;
510 image_.launch_requested = false;
511 manifest_.last_status = DFUStatusCode::OK;
512 download_.session_started = false;
513 download_.received_bytes = 0u;
514 download_.expected_block_num = 0u;
515 upload_.session_started = false;
516 upload_.offset = 0u;
517 upload_.expected_block_num = 0u;
518 upload_.image_size = 0u;
519 manifest_.pending = false;
520 }
521
522 // 计算 seal 记录使用的固定 CRC32,只覆盖 payload 区域。
523 // Compute the fixed CRC32 used by the seal record over the payload area only.
524 bool ComputeImageCrc32(size_t image_size, uint32_t& crc32_out)
525 {
527 {
529 }
530 uint32_t crc = 0xFFFFFFFFu;
531 size_t offset = 0u;
532 while (offset < image_size)
533 {
534 size_t chunk = image_size - offset;
535 if (chunk > sizeof(crc_buffer_))
536 {
537 chunk = sizeof(crc_buffer_);
538 }
539 if (flash_.Read(image_offset_ + offset, {crc_buffer_, chunk}) != ErrorCode::OK)
540 {
541 return false;
542 }
543 const uint8_t* buf = crc_buffer_;
544 size_t remain = chunk;
545 while (remain-- > 0u)
546 {
547 crc = LibXR::CRC32::tab_[(crc ^ *buf++) & 0xFFu] ^ (crc >> 8);
548 }
549 offset += chunk;
550 }
551 crc32_out = crc;
552 return true;
553 }
554
555 // 从 flash 中读取当前 seal 记录。
556 // Read the current seal record from flash.
557 bool ReadSeal(SealRecord& seal)
558 {
559 return flash_.Read(image_offset_ + seal_offset_, {reinterpret_cast<uint8_t*>(&seal),
560 sizeof(seal)}) == ErrorCode::OK;
561 }
562
563 // Seal may share the last erase block with payload. Reuse the per-session
564 // erase map so manifest does not wipe a block that was already erased and
565 // partially programmed by DNLOAD payload chunks.
566 bool WriteSeal(size_t image_size, uint32_t crc32)
567 {
568 std::memset(seal_storage_, 0xFF, seal_storage_size_);
569 auto* seal = reinterpret_cast<SealRecord*>(seal_storage_);
570 seal->magic = SEAL_MAGIC;
571 seal->image_size = static_cast<uint32_t>(image_size);
572 seal->crc32 = crc32;
573 seal->crc32_inv = ~crc32;
574
575 if (!EnsureBlocksErased(seal_offset_, seal_storage_size_))
576 {
577 return false;
578 }
579 return flash_.Write(image_offset_ + seal_offset_,
580 {seal_storage_, seal_storage_size_}) == ErrorCode::OK;
581 }
582
583 // 探测 flash 当前是否已有有效的已封印镜像,
584 // 并可选返回它记录的 payload 大小。
585 // Probe whether flash already contains a valid sealed image, and optionally
586 // return its stored payload size.
587 bool ProbeStoredImage(size_t* out_image_size)
588 {
589 SealRecord seal = {};
590 if (!ReadSeal(seal))
591 {
592 return false;
593 }
594 if (seal.magic != SEAL_MAGIC)
595 {
596 return false;
597 }
598 if ((seal.crc32 ^ seal.crc32_inv) != 0xFFFFFFFFu)
599 {
600 return false;
601 }
602 const size_t image_size = static_cast<size_t>(seal.image_size);
603 if (image_size == 0u || image_size > PayloadLimit())
604 {
605 return false;
606 }
607 uint32_t actual_crc = 0u;
608 if (!ComputeImageCrc32(image_size, actual_crc))
609 {
610 return false;
611 }
612 if (actual_crc != seal.crc32)
613 {
614 return false;
615 }
616 if (out_image_size != nullptr)
617 {
618 *out_image_size = image_size;
619 }
620 return true;
621 }
622
623 // 对 [offset, offset+len) 覆盖到的每个块,
624 // 在一次下载会话里最多只擦除一次。
625 // Erase every block touched by [offset, offset+len) at most once per
626 // download session.
627 bool EnsureBlocksErased(size_t offset, size_t len)
628 {
629 if (len == 0u)
630 {
631 return true;
632 }
633
634 const size_t first_block = offset / erase_block_size_;
635 const size_t last_block = (offset + len - 1u) / erase_block_size_;
636 if (last_block >= erase_block_count_)
637 {
638 return false;
639 }
640
641 for (size_t block = first_block; block <= last_block; ++block)
642 {
643 if (erased_blocks_[block] != 0u)
644 {
645 continue;
646 }
647 const size_t block_offset = block * erase_block_size_;
648 if (flash_.Erase(image_offset_ + block_offset, erase_block_size_) != ErrorCode::OK)
649 {
650 return false;
651 }
652 erased_blocks_[block] = 1u;
653 }
654 return true;
655 }
656
661 struct ImageState
662 {
663 bool launch_requested = false;
664 bool ready = false;
665 size_t stored_size = 0u;
666 };
667
671 struct DownloadState
672 {
673 bool session_started = false;
674 size_t received_bytes = 0u;
675 uint16_t expected_block_num = 0u;
676 uint32_t next_poll_timeout_ms = 0u;
677 DFUStatusCode last_status = DFUStatusCode::OK;
678 };
679
683 struct WriteState
684 {
685 size_t offset = 0u;
686 size_t len = 0u;
687 uint16_t block_num = 0u;
688 bool pending = false;
689 };
690
694 struct ManifestState
695 {
696 bool pending = false;
697 uint32_t poll_timeout_ms = 50u;
698 DFUStatusCode last_status = DFUStatusCode::OK;
699 };
700
704 struct UploadState
705 {
706 bool session_started = false;
707 size_t offset = 0u;
708 uint16_t expected_block_num = 0u;
709 size_t image_size = 0u;
710 };
711
712 Flash& flash_;
713 size_t image_offset_ = 0u;
714 size_t image_size_limit_ = 0u;
715 size_t seal_offset_ = 0u;
716 JumpCallback jump_to_app_ = nullptr;
717 void* jump_app_ctx_ = nullptr;
718 bool autorun_ = true;
719 ImageState image_ = {};
720 size_t erase_block_size_ = 1u;
721 size_t erase_block_count_ = 0u;
722 uint8_t* erased_blocks_ = nullptr;
723 size_t seal_storage_size_ = 0u;
724 uint8_t* seal_storage_ = nullptr;
725 size_t transfer_size_ = 0u;
726 uint8_t* write_buffer_ = nullptr;
727 uint8_t crc_buffer_[256] = {};
728 DownloadState download_ = {};
729 WriteState write_ = {};
730 ManifestState manifest_ = {};
731 UploadState upload_ = {};
732};
733
739template <typename Backend, size_t MAX_TRANSFER_SIZE = 4096u>
741{
742 static_assert(MAX_TRANSFER_SIZE > 0u, "DFU transfer size must be non-zero.");
743 static_assert(MAX_TRANSFER_SIZE <= 0xFFFFu,
744 "DFU transfer size must fit in wTransferSize.");
745
746 static constexpr uint8_t INTERFACE_CLASS = 0xFEu;
747 static constexpr uint8_t INTERFACE_SUB_CLASS = 0x01u;
748 static constexpr uint8_t INTERFACE_PROTOCOL = 0x02u;
749 static constexpr uint16_t DFU_VERSION = 0x0110u;
750
751 static constexpr uint8_t ATTR_CAN_DOWNLOAD = 0x01u;
752 static constexpr uint8_t ATTR_CAN_UPLOAD = 0x02u;
753 static constexpr uint8_t ATTR_MANIFESTATION_TOLERANT = 0x04u;
754 static constexpr uint8_t ATTR_WILL_DETACH = 0x08u;
755
756 public:
757 LIBXR_PACKED_BEGIN
763 {
764 uint8_t bLength = 9;
765 uint8_t bDescriptorType = 0x21;
766 uint8_t bmAttributes = 0;
767 uint16_t wDetachTimeOut = 0;
768 uint16_t wTransferSize = 0;
769 uint16_t bcdDFUVersion = DFU_VERSION;
770 };
771
776 {
777 uint8_t bStatus = 0;
778 uint8_t bwPollTimeout[3] = {0, 0, 0};
779 uint8_t bState = 0;
780 uint8_t iString = 0;
781
782 void SetPollTimeout(uint32_t timeout_ms)
783 {
784 bwPollTimeout[0] = static_cast<uint8_t>(timeout_ms & 0xFFu);
785 bwPollTimeout[1] = static_cast<uint8_t>((timeout_ms >> 8) & 0xFFu);
786 bwPollTimeout[2] = static_cast<uint8_t>((timeout_ms >> 16) & 0xFFu);
787 }
788 };
789
794 {
796 9,
797 static_cast<uint8_t>(DescriptorType::INTERFACE),
798 0,
799 0,
800 0,
801 INTERFACE_CLASS,
802 INTERFACE_SUB_CLASS,
803 INTERFACE_PROTOCOL,
804 0};
805 FunctionalDescriptor func_desc = {};
806 };
807 LIBXR_PACKED_END
808
809 static_assert(sizeof(FunctionalDescriptor) == 9,
810 "DFU functional descriptor size mismatch.");
811 static_assert(sizeof(StatusResponse) == 6, "DFU status response size mismatch.");
812
813 public:
814 static constexpr const char* DEFAULT_INTERFACE_STRING = "XRUSB DFU";
815 static constexpr const char* DEFAULT_WINUSB_DEVICE_INTERFACE_GUID =
816 DfuInterfaceClassBase::DEFAULT_WINUSB_DEVICE_INTERFACE_GUID;
817 static constexpr uint8_t DEFAULT_WINUSB_VENDOR_CODE =
818 DfuInterfaceClassBase::DEFAULT_WINUSB_VENDOR_CODE;
819
837 explicit DFUClass(
838 Backend& backend, const char* interface_string = DEFAULT_INTERFACE_STRING,
839 const char* webusb_landing_page_url = nullptr,
840 uint8_t webusb_vendor_code = LibXR::USB::WebUsb::WEBUSB_VENDOR_CODE_DEFAULT,
841 const char* winusb_device_interface_guid = DEFAULT_WINUSB_DEVICE_INTERFACE_GUID,
842 uint8_t winusb_vendor_code = DEFAULT_WINUSB_VENDOR_CODE)
843 : DfuInterfaceClassBase(interface_string, webusb_landing_page_url,
844 webusb_vendor_code, winusb_device_interface_guid,
845 winusb_vendor_code),
846 backend_(backend)
847 {
848 }
849
850 protected:
851 void BindEndpoints(EndpointPool&, uint8_t start_itf_num, bool) override
852 {
853 // 固件态 DFU 在绑定阶段发布单接口描述符,并校验 backend 能力。
854 // Firmware DFU publishes one interface and validates backend capabilities during
855 // bind.
856 interface_num_ = start_itf_num;
857 current_alt_setting_ = 0u;
858
859 caps_ = backend_.GetDfuCapabilities();
860 if (caps_.transfer_size == 0u || caps_.transfer_size > MAX_TRANSFER_SIZE)
861 {
862 caps_.transfer_size = static_cast<uint16_t>(MAX_TRANSFER_SIZE);
863 }
864
865 desc_block_.interface_desc.bInterfaceNumber = interface_num_;
866 desc_block_.interface_desc.bAlternateSetting = 0u;
867 desc_block_.interface_desc.iInterface = GetInterfaceStringIndex(0u);
868 desc_block_.func_desc.bmAttributes = BuildAttributeBitmap(caps_);
869 desc_block_.func_desc.wDetachTimeOut = caps_.detach_timeout_ms;
870 desc_block_.func_desc.wTransferSize = caps_.transfer_size;
871 desc_block_.func_desc.bcdDFUVersion = DFU_VERSION;
872
873 SetData(RawData{reinterpret_cast<uint8_t*>(&desc_block_), sizeof(desc_block_)});
874
875 ResetProtocolState();
876 auto ec = backend_.DfuSetAlternate(current_alt_setting_);
877 if (ec != ErrorCode::OK)
878 {
879 inited_ = false;
880 return;
881 }
882 inited_ = true;
883 }
884
885 void UnbindEndpoints(EndpointPool&, bool) override
886 {
887 if (inited_)
888 {
889 // 解绑时先把 backend 置回 abort 状态,
890 // 这样下一次绑定总是从干净的协议会话开始。
891 // Unbind must leave the backend in an aborted state so the next bind
892 // always starts from a clean protocol session.
893 backend_.DfuAbort(current_alt_setting_);
894 }
895 inited_ = false;
896 ResetProtocolState();
897 }
898
899 size_t GetInterfaceCount() override { return 1u; }
900 bool HasIAD() override { return false; }
901 size_t GetMaxConfigSize() override { return sizeof(desc_block_); }
903 {
905 header.data_.bDeviceSubClass = INTERFACE_SUB_CLASS;
906 header.data_.bDeviceProtocol = INTERFACE_PROTOCOL;
907 return ErrorCode::OK;
908 }
909
910 ErrorCode SetAltSetting(uint8_t itf, uint8_t alt) override
911 {
912 if (itf != interface_num_)
913 {
915 }
916 if (alt != 0u)
917 {
919 }
920 if (state_ == DFUState::DFU_DNBUSY || state_ == DFUState::DFU_MANIFEST ||
921 state_ == DFUState::DFU_MANIFEST_WAIT_RESET)
922 {
923 // 协议仍处于非空闲阶段时,不允许切换 alternate setting。
924 // Alternate setting cannot change while the protocol is in a non-idle phase.
925 return ErrorCode::BUSY;
926 }
927
928 auto ec = backend_.DfuSetAlternate(alt);
929 if (ec != ErrorCode::OK)
930 {
931 return ec;
932 }
933
934 backend_.DfuAbort(current_alt_setting_);
935 current_alt_setting_ = alt;
936 ClearErrorState();
937 return ErrorCode::OK;
938 }
939
940 ErrorCode GetAltSetting(uint8_t itf, uint8_t& alt) override
941 {
942 if (itf != interface_num_)
943 {
945 }
946 alt = current_alt_setting_;
947 return ErrorCode::OK;
948 }
949
950 ErrorCode OnGetDescriptor(bool, uint8_t, uint16_t wValue, uint16_t,
951 ConstRawData& out_data) override
952 {
953 const uint8_t desc_type = static_cast<uint8_t>((wValue >> 8) & 0xFFu);
954 if (desc_type != desc_block_.func_desc.bDescriptorType)
955 {
957 }
958
959 out_data = {reinterpret_cast<const uint8_t*>(&desc_block_.func_desc),
960 sizeof(desc_block_.func_desc)};
961 return ErrorCode::OK;
962 }
963
964 ErrorCode OnClassRequest(bool, uint8_t bRequest, uint16_t wValue, uint16_t wLength,
965 uint16_t wIndex, ControlTransferResult& result) override
966 {
967 if (!inited_)
968 {
969 return ErrorCode::INIT_ERR;
970 }
971 if ((wIndex & 0xFFu) != interface_num_)
972 {
974 }
975
976 switch (static_cast<DFURequest>(bRequest))
977 {
978 case DFURequest::DETACH:
979 return HandleDetach(result);
980
981 case DFURequest::DNLOAD:
982 return HandleDnload(wValue, wLength, result);
983
984 case DFURequest::UPLOAD:
985 return HandleUpload(wValue, wLength, result);
986
987 case DFURequest::GETSTATUS:
988 return HandleGetStatus(result);
989
990 case DFURequest::CLRSTATUS:
991 return HandleClearStatus(result);
992
993 case DFURequest::GETSTATE:
994 return HandleGetState(result);
995
996 case DFURequest::ABORT:
997 return HandleAbort(result);
998
999 default:
1000 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1001 }
1002 }
1003
1004 ErrorCode OnClassData(bool, uint8_t bRequest, LibXR::ConstRawData& data) override
1005 {
1006 const auto request = static_cast<DFURequest>(bRequest);
1007 UNUSED(data);
1008
1009 if (request != DFURequest::DNLOAD)
1010 {
1012 }
1013
1014 if (pending_dnload_length_ == 0u)
1015 {
1016 // 这里出现零长度 OUT data stage 只在“DNLOAD 已结束”的路径上才是合法的。
1017 // A zero-length OUT data stage is legal here only for the already-finished
1018 // DNLOAD path.
1019 return ErrorCode::OK;
1020 }
1021
1022 uint32_t poll_timeout_ms = 0u;
1023 auto status = backend_.DfuDownload(current_alt_setting_, pending_block_num_, data,
1024 poll_timeout_ms);
1025 pending_dnload_length_ = 0u;
1026 poll_timeout_ms_ = poll_timeout_ms;
1027
1028 if (status == DFUStatusCode::OK)
1029 {
1030 state_ = DFUState::DFU_DNLOAD_SYNC;
1031 status_ = DFUStatusCode::OK;
1032 download_started_ = true;
1033 }
1034 else
1035 {
1036 EnterErrorState(status);
1037 }
1038 return ErrorCode::OK;
1039 }
1040
1041 void OnClassInDataStatusComplete(bool, uint8_t bRequest) override
1042 {
1043 if (static_cast<DFURequest>(bRequest) != DFURequest::GETSTATUS)
1044 {
1045 return;
1046 }
1047 if (state_ == DFUState::DFU_MANIFEST_WAIT_RESET)
1048 {
1049 // 只有在主机完成了报告 MANIFEST-WAIT-RESET 的那笔 GETSTATUS 的 STATUS OUT 后,
1050 // 才真正提交 autorun。
1051 // Commit autorun only after the host completes the STATUS OUT of the
1052 // GETSTATUS request that reported MANIFEST-WAIT-RESET.
1053 backend_.DfuCommitManifestWaitReset(current_alt_setting_);
1054 }
1055 }
1056
1057 private:
1058 static constexpr uint8_t BuildAttributeBitmap(const DFUCapabilities& caps)
1059 {
1060 return static_cast<uint8_t>(
1061 (caps.can_download ? ATTR_CAN_DOWNLOAD : 0u) |
1062 (caps.can_upload ? ATTR_CAN_UPLOAD : 0u) |
1063 (caps.manifestation_tolerant ? ATTR_MANIFESTATION_TOLERANT : 0u) |
1064 (caps.will_detach ? ATTR_WILL_DETACH : 0u));
1065 }
1066
1067 ErrorCode HandleDetach(ControlTransferResult&)
1068 {
1069 // 固件态 DFU 不实现 DETACH;该请求由 runtime DFU 处理。
1070 // Firmware-mode DFU does not implement DETACH; runtime DFU owns that request.
1071 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1072 }
1073
1074 ErrorCode HandleDnload(uint16_t block_num, uint16_t wLength,
1075 ControlTransferResult& result)
1076 {
1077 if (!caps_.can_download)
1078 {
1079 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1080 }
1081 if (!(state_ == DFUState::DFU_IDLE || state_ == DFUState::DFU_DNLOAD_IDLE))
1082 {
1083 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1084 }
1085 if (wLength > caps_.transfer_size)
1086 {
1087 return ProtocolStall(DFUStatusCode::ERR_USBR);
1088 }
1089
1090 if (wLength == 0u)
1091 {
1092 // 零长度 DNLOAD 表示结束数据阶段并进入 manifest,
1093 // 但前提是之前至少成功接收过一个 payload block。
1094 // A zero-length DNLOAD terminates the data phase and requests manifest,
1095 // but only after at least one payload block was accepted.
1096 pending_dnload_length_ = 0u;
1097 pending_block_num_ = block_num;
1098 if (!download_started_)
1099 {
1100 EnterErrorState(DFUStatusCode::ERR_NOTDONE);
1101 }
1102 else
1103 {
1104 state_ = DFUState::DFU_MANIFEST_SYNC;
1105 status_ = DFUStatusCode::OK;
1106 poll_timeout_ms_ = 0u;
1107 }
1108 result.SendStatusInZLP() = true;
1109 return ErrorCode::OK;
1110 }
1111
1112 pending_block_num_ = block_num;
1113 pending_dnload_length_ = wLength;
1114 result.OutData() = {transfer_buffer_, wLength};
1115 return ErrorCode::OK;
1116 }
1117
1118 ErrorCode HandleUpload(uint16_t block_num, uint16_t wLength,
1119 ControlTransferResult& result)
1120 {
1121 if (!caps_.can_upload)
1122 {
1123 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1124 }
1125 if (!(state_ == DFUState::DFU_IDLE || state_ == DFUState::DFU_UPLOAD_IDLE))
1126 {
1127 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1128 }
1129 if (wLength == 0u)
1130 {
1131 return ProtocolStall(DFUStatusCode::ERR_USBR);
1132 }
1133
1134 size_t req_size = wLength;
1135 if (req_size > caps_.transfer_size)
1136 {
1137 req_size = caps_.transfer_size;
1138 }
1139
1140 DFUStatusCode op_status = DFUStatusCode::OK;
1141 uint32_t poll_timeout_ms = 0u;
1142 const size_t read_size =
1143 backend_.DfuUpload(current_alt_setting_, block_num, {transfer_buffer_, req_size},
1144 op_status, poll_timeout_ms);
1145
1146 poll_timeout_ms_ = poll_timeout_ms;
1147 if (op_status != DFUStatusCode::OK)
1148 {
1149 return ProtocolStall(op_status);
1150 }
1151 if (read_size > req_size)
1152 {
1153 return ProtocolStall(DFUStatusCode::ERR_USBR);
1154 }
1155
1156 status_ = DFUStatusCode::OK;
1157 state_ = (read_size < req_size) ? DFUState::DFU_IDLE : DFUState::DFU_UPLOAD_IDLE;
1158 result.InData() = {transfer_buffer_, read_size};
1159 return ErrorCode::OK;
1160 }
1161
1162 ErrorCode HandleGetStatus(ControlTransferResult& result)
1163 {
1164 AdvanceStateForStatusRead();
1165 status_response_.bStatus = static_cast<uint8_t>(status_);
1166 status_response_.SetPollTimeout(poll_timeout_ms_);
1167 status_response_.bState = static_cast<uint8_t>(state_);
1168 status_response_.iString = 0u;
1169 result.InData() = {reinterpret_cast<const uint8_t*>(&status_response_),
1170 sizeof(status_response_)};
1171 return ErrorCode::OK;
1172 }
1173
1174 ErrorCode HandleClearStatus(ControlTransferResult& result)
1175 {
1176 if (state_ != DFUState::DFU_ERROR)
1177 {
1178 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1179 }
1180 backend_.DfuClearStatus(current_alt_setting_);
1181 ClearErrorState();
1182 result.SendStatusInZLP() = true;
1183 return ErrorCode::OK;
1184 }
1185
1186 ErrorCode HandleGetState(ControlTransferResult& result)
1187 {
1188 state_response_ = static_cast<uint8_t>(state_);
1189 result.InData() = {&state_response_, sizeof(state_response_)};
1190 return ErrorCode::OK;
1191 }
1192
1193 ErrorCode HandleAbort(ControlTransferResult& result)
1194 {
1195 switch (state_)
1196 {
1197 case DFUState::DFU_IDLE:
1198 case DFUState::DFU_DNLOAD_SYNC:
1199 case DFUState::DFU_DNLOAD_IDLE:
1200 case DFUState::DFU_UPLOAD_IDLE:
1201 case DFUState::DFU_MANIFEST_SYNC:
1202 backend_.DfuAbort(current_alt_setting_);
1203 ClearErrorState();
1204 result.SendStatusInZLP() = true;
1205 return ErrorCode::OK;
1206
1207 default:
1208 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1209 }
1210 }
1211
1212 void AdvanceStateForStatusRead()
1213 {
1214 // 标准 DFU 状态机是在 GETSTATUS 这个同步点上推进的。
1215 // GETSTATUS is the synchronization point that advances the standard DFU state
1216 // machine.
1217 switch (state_)
1218 {
1219 case DFUState::DFU_DNLOAD_SYNC:
1220 RefreshDownloadStatus();
1221 break;
1222
1223 case DFUState::DFU_MANIFEST_SYNC:
1224 {
1225 if (status_ != DFUStatusCode::OK)
1226 {
1227 state_ = DFUState::DFU_ERROR;
1228 break;
1229 }
1230
1231 uint32_t poll_timeout_ms = 0u;
1232 auto manifest_status =
1233 backend_.DfuManifest(current_alt_setting_, poll_timeout_ms);
1234 poll_timeout_ms_ = poll_timeout_ms;
1235 if (manifest_status == DFUStatusCode::OK)
1236 {
1237 status_ = DFUStatusCode::OK;
1238 state_ = DFUState::DFU_MANIFEST;
1239 }
1240 else
1241 {
1242 EnterErrorState(manifest_status);
1243 }
1244 break;
1245 }
1246
1247 case DFUState::DFU_DNBUSY:
1248 RefreshDownloadStatus();
1249 break;
1250
1251 case DFUState::DFU_MANIFEST:
1252 RefreshManifestStatus();
1253 break;
1254
1255 default:
1256 break;
1257 }
1258 }
1259
1260 void RefreshDownloadStatus()
1261 {
1262 if (status_ != DFUStatusCode::OK)
1263 {
1264 state_ = DFUState::DFU_ERROR;
1265 return;
1266 }
1267
1268 bool busy = false;
1269 uint32_t poll_timeout_ms = 0u;
1270 const auto download_status =
1271 backend_.DfuGetDownloadStatus(current_alt_setting_, busy, poll_timeout_ms);
1272 poll_timeout_ms_ = poll_timeout_ms;
1273 if (download_status != DFUStatusCode::OK)
1274 {
1275 EnterErrorState(download_status);
1276 return;
1277 }
1278
1279 // backend 报 busy 时映射到 DFU_DNBUSY;
1280 // 否则主机可以从 DFU_DNLOAD_IDLE 继续发送下一块 DNLOAD。
1281 // A backend-reported busy maps to DFU_DNBUSY; otherwise the host may
1282 // continue sending the next DNLOAD block from DFU_DNLOAD_IDLE.
1283 state_ = busy ? DFUState::DFU_DNBUSY : DFUState::DFU_DNLOAD_IDLE;
1284 }
1285
1286 void RefreshManifestStatus()
1287 {
1288 if (status_ != DFUStatusCode::OK)
1289 {
1290 state_ = DFUState::DFU_ERROR;
1291 return;
1292 }
1293
1294 bool busy = false;
1295 uint32_t poll_timeout_ms = 0u;
1296 const auto manifest_status =
1297 backend_.DfuGetManifestStatus(current_alt_setting_, busy, poll_timeout_ms);
1298 poll_timeout_ms_ = poll_timeout_ms;
1299 if (manifest_status != DFUStatusCode::OK)
1300 {
1301 EnterErrorState(manifest_status);
1302 return;
1303 }
1304
1305 if (busy)
1306 {
1307 state_ = DFUState::DFU_MANIFEST;
1308 return;
1309 }
1310
1311 // manifest 完成后,要么回到 IDLE(tolerant),要么进入 WAIT_RESET(non-tolerant)。
1312 // Manifest completion returns either to IDLE (tolerant) or WAIT_RESET (non-tolerant).
1313 download_started_ = false;
1314 state_ = caps_.manifestation_tolerant ? DFUState::DFU_IDLE
1315 : DFUState::DFU_MANIFEST_WAIT_RESET;
1316 }
1317
1318 void ResetProtocolState()
1319 {
1320 // 这里只重置前端拥有的协议状态;
1321 // 镜像级 bookkeeping 保留在 backend_ 里。
1322 // Reset only frontend-owned protocol state;
1323 // image-level bookkeeping stays in backend_.
1324 pending_block_num_ = 0u;
1325 pending_dnload_length_ = 0u;
1326 poll_timeout_ms_ = 0u;
1327 current_alt_setting_ = 0u;
1328 download_started_ = false;
1329 ClearErrorState();
1330 }
1331
1332 void ClearErrorState()
1333 {
1334 status_ = DFUStatusCode::OK;
1335 state_ = DFUState::DFU_IDLE;
1336 poll_timeout_ms_ = 0u;
1337 }
1338
1339 void EnterErrorState(DFUStatusCode status)
1340 {
1341 // 一旦锁存错误,前端会保持在 DFU_ERROR,
1342 // 直到 CLRSTATUS/ABORT 把它清掉。
1343 // Once an error is latched, the frontend stays in DFU_ERROR
1344 // until CLRSTATUS/ABORT clears it.
1345 status_ = status;
1346 state_ = DFUState::DFU_ERROR;
1347 poll_timeout_ms_ = 0u;
1348 }
1349
1350 ErrorCode ProtocolStall(DFUStatusCode status)
1351 {
1352 EnterErrorState(status);
1353 return ErrorCode::ARG_ERR;
1354 }
1355
1356 private:
1357 Backend& backend_;
1358 DFUCapabilities caps_ = {};
1359 DescriptorBlock desc_block_ = {};
1360 StatusResponse status_response_ = {};
1361 uint8_t state_response_ = 0u;
1362 uint8_t transfer_buffer_[MAX_TRANSFER_SIZE] =
1363 {};
1364 bool download_started_ = false;
1365 uint16_t pending_block_num_ = 0u;
1366 uint16_t pending_dnload_length_ = 0u;
1367 uint32_t poll_timeout_ms_ = 0u;
1368 DFUState state_ = DFUState::DFU_IDLE;
1369 DFUStatusCode status_ = DFUStatusCode::OK;
1370};
1371
1377{
1378 protected:
1379 using JumpCallback = DfuBootloaderBackend::JumpCallback;
1380
1381 DfuBootloaderClassStorage(Flash& flash, size_t image_base, size_t image_limit,
1382 size_t seal_offset, JumpCallback jump_to_app,
1383 void* jump_app_ctx = nullptr, bool autorun = true)
1384 : backend_(flash, image_base, image_limit, seal_offset, jump_to_app, jump_app_ctx,
1385 autorun),
1386 image_base_(image_base),
1387 image_limit_(image_limit),
1388 seal_offset_(seal_offset)
1389 {
1390 }
1391
1392 DfuBootloaderBackend backend_;
1393 size_t image_base_ = 0u;
1394 size_t image_limit_ = 0u;
1395 size_t seal_offset_ = 0u;
1396};
1397
1402template <size_t MAX_TRANSFER_SIZE = 4096u>
1404 public DFUClass<DfuBootloaderBackend, MAX_TRANSFER_SIZE>
1405{
1408
1409 public:
1410 using JumpCallback = DfuBootloaderBackend::JumpCallback;
1411 static constexpr uint8_t VENDOR_REQUEST_RUN_APP = 0x5Au;
1412
1414 Flash& flash, size_t image_base, size_t image_limit, size_t seal_offset,
1415 JumpCallback jump_to_app, void* jump_app_ctx = nullptr, bool autorun = true,
1416 const char* interface_string = Base::DEFAULT_INTERFACE_STRING,
1417 const char* webusb_landing_page_url = nullptr,
1418 uint8_t webusb_vendor_code = LibXR::USB::WebUsb::WEBUSB_VENDOR_CODE_DEFAULT,
1419 const char* winusb_device_interface_guid =
1420 Base::DEFAULT_WINUSB_DEVICE_INTERFACE_GUID,
1421 uint8_t winusb_vendor_code = Base::DEFAULT_WINUSB_VENDOR_CODE)
1422 : Storage(flash, image_base, image_limit, seal_offset, jump_to_app, jump_app_ctx,
1423 autorun),
1424 Base(Storage::backend_, interface_string, webusb_landing_page_url,
1425 webusb_vendor_code, winusb_device_interface_guid, winusb_vendor_code)
1426 {
1427 }
1428
1429 // 这里只推进 backend 拥有的异步工作;真正跳 app 仍保持显式调用。
1430 // Process only backend-owned async work; actual app launch remains explicit.
1431 void Process() { Storage::backend_.Process(); }
1432 bool RequestRunApp() { return Storage::backend_.TryRequestRunApp(); }
1433
1434 bool TryConsumeAppLaunch(uint32_t now_ms)
1435 {
1436 return Storage::backend_.TryConsumeAppLaunch(now_ms);
1437 }
1438
1439 bool HasPendingWork() const { return Storage::backend_.HasPendingWork(); }
1440 bool HasValidImage() const { return Storage::backend_.HasValidImage(); }
1441 size_t ImageSize() const { return Storage::backend_.ImageSize(); }
1442 size_t ImageBase() const { return Storage::image_base_; }
1443 size_t ImageLimit() const { return Storage::image_limit_; }
1444 size_t SealOffset() const { return Storage::seal_offset_; }
1445
1446 protected:
1447 ErrorCode OnVendorRequest(bool, uint8_t bRequest, uint16_t wValue, uint16_t wLength,
1448 uint16_t,
1449 typename Base::ControlTransferResult& result) override
1450 {
1451 if (bRequest != VENDOR_REQUEST_RUN_APP)
1452 {
1454 }
1455 if (wLength != 0u || wValue != 0u)
1456 {
1457 return ErrorCode::ARG_ERR;
1458 }
1459 if (Storage::backend_.HasPendingWork())
1460 {
1461 // 协议拥有的异步工作尚未完成时,不允许启动 app。
1462 // Do not launch the app while protocol-owned async work is still pending.
1463 return ErrorCode::BUSY;
1464 }
1465 if (!Storage::backend_.TryRequestRunApp())
1466 {
1467 // RUN_APP 只在已知存在有效 seal 镜像时才会接受。
1468 // RUN_APP is only accepted when a valid sealed image is known.
1469 return ErrorCode::FAILED;
1470 }
1471 result.SendStatusInZLP() = true;
1472 return ErrorCode::OK;
1473 }
1474};
1475
1476using DfuBootloaderClass = DfuBootloaderClassT<4096u>;
1477
1478} // namespace LibXR::USB
static bool inited_
查找表是否已初始化 / Whether the lookup table is initialized
Definition crc.hpp:190
static uint32_t tab_[256]
CRC32 查找表 / CRC32 lookup table.
Definition crc.hpp:189
static void GenerateTable()
生成 CRC32 查找表 / Generates the CRC32 lookup table
Definition crc.hpp:198
只读原始数据视图 / Immutable raw data view
size_t size_
数据字节数 / Data size in bytes
const void * addr_
数据起始地址 / Data start address
抽象基类,表示闪存接口。 Abstract base class representing a flash memory interface.
Definition flash.hpp:32
size_t MinWriteSize() const
获取最小可写块大小(字节) / Returns the minimum writable block size in bytes.
Definition flash.hpp:83
virtual ErrorCode Erase(size_t offset, size_t size)=0
擦除闪存的指定区域 / Erases a section of the flash memory.
virtual ErrorCode Write(size_t offset, ConstRawData data)=0
向闪存写入数据 / Writes data to the flash memory.
virtual ErrorCode Read(size_t offset, RawData data)
从闪存中读取数据 / Reads data from the flash memory.
Definition flash.cpp:14
size_t MinEraseSize() const
获取最小可擦除块大小(字节)。 Returns the minimum erasable block size in bytes.
Definition flash.hpp:76
可写原始数据视图 / Mutable raw data view
void SetData(RawData data)
设置内部数据缓存 / Set internal data cache
Definition desc_cfg.hpp:207
通用 DFU 前端:实现标准 DFU 状态机,backend 只负责存储细节 Generic DFU frontend: implements the standard DFU state machin...
ErrorCode SetAltSetting(uint8_t itf, uint8_t alt) override
可选:设置接口备用设置 / Optional: set interface alternate setting
bool HasIAD() override
是否包含 IAD / Whether an IAD is used
ErrorCode OnClassData(bool, uint8_t bRequest, LibXR::ConstRawData &data) override
处理 Class request 数据阶段 / Handle class request data stage
ErrorCode WriteDeviceDescriptor(DeviceDescriptor &header) override
可选:覆盖设备描述符字段 / Optional: override device descriptor fields
size_t GetInterfaceCount() override
接口数量 / Number of interfaces contributed
void UnbindEndpoints(EndpointPool &, bool) override
解绑端点资源 / Unbind endpoint resources
DFUClass(Backend &backend, 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, const char *winusb_device_interface_guid=DEFAULT_WINUSB_DEVICE_INTERFACE_GUID, uint8_t winusb_vendor_code=DEFAULT_WINUSB_VENDOR_CODE)
Backend 需要满足的接口契约 / Backend contract requirements.
ErrorCode OnGetDescriptor(bool, uint8_t, uint16_t wValue, uint16_t, ConstRawData &out_data) override
处理标准请求 GET_DESCRIPTOR(类特定描述符) Handle standard GET_DESCRIPTOR request (class-specific descriptors).
void BindEndpoints(EndpointPool &, uint8_t start_itf_num, bool) override
绑定端点资源 / Bind endpoint resources
size_t GetMaxConfigSize() override
最大配置描述符占用 / Maximum bytes required in configuration descriptor
ErrorCode GetAltSetting(uint8_t itf, uint8_t &alt) override
可选:获取接口备用设置 / Optional: get interface alternate setting
void OnClassInDataStatusComplete(bool, uint8_t bRequest) override
类请求的 IN 数据阶段在 STATUS OUT 完成后回调 Called after the STATUS OUT completes for a Class IN data request.
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)
uint8_t GetInterfaceStringIndex(size_t local_interface_index) const
返回已分配的接口字符串索引 Return the assigned USB string index for a local interface.
USB描述符基类 USB descriptor base class.
Definition desc_dev.hpp:39
Data data_
设备描述符数据实例 / Internal data instance
Definition desc_dev.hpp:108
@ APPLICATION_SPECIFIC
应用专用类 / Application Specific
Bootloader DFU 的通用后端:围绕 Flash 基类实现 download/upload/manifest Generic bootloader DFU backend built arou...
DFUStatusCode DfuGetManifestStatus(uint8_t alt, bool &busy, uint32_t &poll_timeout_ms)
查询 manifest 异步状态 / Query manifest async status
void DfuClearStatus(uint8_t)
清除 DFU 错误态 / Clear the DFU error state
DFUStatusCode DfuManifest(uint8_t alt, uint32_t &poll_timeout_ms)
启动 manifest 阶段 / Start the manifest stage
DFUCapabilities GetDfuCapabilities() const
报告 DFU 能力集 / Report DFU capabilities
void DfuAbort(uint8_t)
放弃当前协议会话 / Abort the current protocol session
把 backend 与前端 DFUClass 组装在一起的存储基类 Storage base that assembles the backend and the DFU frontend class.
面向单镜像 bootloader 区的 DFU 类 Bootloader DFU class for a single image region.
ErrorCode OnVendorRequest(bool, uint8_t bRequest, uint16_t wValue, uint16_t wLength, uint16_t, typename Base::ControlTransferResult &result) override
处理 Vendor request(Setup stage)/ Handle vendor request (Setup stage)
DFU 单接口类公共基类 / Common base for single-interface DFU classes.
Definition dfu_def.hpp:87
USB端点池类 / USB endpoint pool class.
Definition ep_pool.hpp:29
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ INIT_ERR
初始化错误 / Initialization error
@ BUSY
忙碌 / Busy
@ NOT_FOUND
未找到 / Not found
@ NOT_SUPPORT
不支持 / Not supported
@ FAILED
操作失败 / Operation failed
@ 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 bAlternateSetting
备用设置号 / Alternate setting
Definition desc_cfg.hpp:79
uint8_t bInterfaceNumber
接口号 / Interface number
Definition desc_cfg.hpp:78
DFU 功能能力集合 / DFU functional capability set.
Definition dfu_def.hpp:74
Bootloader DFU 的接口描述符块 / Bootloader DFU descriptor block.
DFU Functional Descriptor(固件模式) DFU Functional Descriptor for firmware mode.
GETSTATUS 返回包 / GETSTATUS response payload.
控制请求(Class/Vendor)处理结果 / Control request (Class/Vendor) handling result
ClassID bDeviceClass
设备类代码 / Device class code
Definition desc_dev.hpp:92
uint8_t bDeviceSubClass
设备子类代码 / Device subclass code
Definition desc_dev.hpp:93
uint8_t bDeviceProtocol
协议代码 / Protocol code
Definition desc_dev.hpp:94
seal 区固定记录 / Fixed record stored in the seal region