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