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
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
19LIBXR_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 };
30LIBXR_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 {
52 if (erase_block_size_ == 0u)
53 {
55 }
57 if (erase_block_count_ > 0u)
58 {
60 std::memset(erased_blocks_, 0, erase_block_count_);
61 }
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 {
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 {
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 }
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_;
649 {
650 return false;
651 }
652 erased_blocks_[block] = 1u;
653 }
654 return true;
655 }
656
662 {
663 bool launch_requested = false;
664 bool ready = false;
665 size_t stored_size = 0u;
666 };
667
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
684 {
685 size_t offset = 0u;
686 size_t len = 0u;
687 uint16_t block_num = 0u;
688 bool pending = false;
689 };
690
695 {
696 bool pending = false;
697 uint32_t poll_timeout_ms = 50u;
698 DFUStatusCode last_status = DFUStatusCode::OK;
699 };
700
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
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;
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] = {};
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:
757LIBXR_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 };
807LIBXR_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;
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 ASSERT(ec == ErrorCode::OK);
878 if (ec != ErrorCode::OK)
879 {
880 inited_ = false;
881 return;
882 }
883 inited_ = true;
884 }
885
886 void UnbindEndpoints(EndpointPool&, bool) override
887 {
888 if (inited_)
889 {
890 // 解绑时先把 backend 置回 abort 状态,
891 // 这样下一次绑定总是从干净的协议会话开始。
892 // Unbind must leave the backend in an aborted state so the next bind
893 // always starts from a clean protocol session.
894 backend_.DfuAbort(current_alt_setting_);
895 }
896 inited_ = false;
897 ResetProtocolState();
898 }
899
900 size_t GetInterfaceCount() override { return 1u; }
901 bool HasIAD() override { return false; }
902 size_t GetMaxConfigSize() override { return sizeof(desc_block_); }
904 {
906 header.data_.bDeviceSubClass = INTERFACE_SUB_CLASS;
907 header.data_.bDeviceProtocol = INTERFACE_PROTOCOL;
908 return ErrorCode::OK;
909 }
910
911 ErrorCode SetAltSetting(uint8_t itf, uint8_t alt) override
912 {
913 if (itf != interface_num_)
914 {
916 }
917 if (alt != 0u)
918 {
920 }
921 if (state_ == DFUState::DFU_DNBUSY || state_ == DFUState::DFU_MANIFEST ||
922 state_ == DFUState::DFU_MANIFEST_WAIT_RESET)
923 {
924 // 协议仍处于非空闲阶段时,不允许切换 alternate setting。
925 // Alternate setting cannot change while the protocol is in a non-idle phase.
926 return ErrorCode::BUSY;
927 }
928
929 auto ec = backend_.DfuSetAlternate(alt);
930 if (ec != ErrorCode::OK)
931 {
932 return ec;
933 }
934
935 backend_.DfuAbort(current_alt_setting_);
936 current_alt_setting_ = alt;
937 ClearErrorState();
938 return ErrorCode::OK;
939 }
940
941 ErrorCode GetAltSetting(uint8_t itf, uint8_t& alt) override
942 {
943 if (itf != interface_num_)
944 {
946 }
947 alt = current_alt_setting_;
948 return ErrorCode::OK;
949 }
950
951 ErrorCode OnGetDescriptor(bool, uint8_t, uint16_t wValue, uint16_t,
952 ConstRawData& out_data) override
953 {
954 const uint8_t desc_type = static_cast<uint8_t>((wValue >> 8) & 0xFFu);
955 if (desc_type != desc_block_.func_desc.bDescriptorType)
956 {
958 }
959
960 out_data = {reinterpret_cast<const uint8_t*>(&desc_block_.func_desc),
961 sizeof(desc_block_.func_desc)};
962 return ErrorCode::OK;
963 }
964
965 ErrorCode OnClassRequest(bool, uint8_t bRequest, uint16_t wValue, uint16_t wLength,
966 uint16_t wIndex, ControlTransferResult& result) override
967 {
968 if (!inited_)
969 {
970 return ErrorCode::INIT_ERR;
971 }
972 if ((wIndex & 0xFFu) != interface_num_)
973 {
975 }
976
977 switch (static_cast<DFURequest>(bRequest))
978 {
979 case DFURequest::DETACH:
980 return HandleDetach(result);
981
982 case DFURequest::DNLOAD:
983 return HandleDnload(wValue, wLength, result);
984
985 case DFURequest::UPLOAD:
986 return HandleUpload(wValue, wLength, result);
987
988 case DFURequest::GETSTATUS:
989 return HandleGetStatus(result);
990
991 case DFURequest::CLRSTATUS:
992 return HandleClearStatus(result);
993
994 case DFURequest::GETSTATE:
995 return HandleGetState(result);
996
997 case DFURequest::ABORT:
998 return HandleAbort(result);
999
1000 default:
1001 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1002 }
1003 }
1004
1005 ErrorCode OnClassData(bool, uint8_t bRequest, LibXR::ConstRawData& data) override
1006 {
1007 const auto request = static_cast<DFURequest>(bRequest);
1008 UNUSED(data);
1009
1010 if (request != DFURequest::DNLOAD)
1011 {
1013 }
1014
1015 if (pending_dnload_length_ == 0u)
1016 {
1017 // 这里出现零长度 OUT data stage 只在“DNLOAD 已结束”的路径上才是合法的。
1018 // A zero-length OUT data stage is legal here only for the already-finished
1019 // DNLOAD path.
1020 return ErrorCode::OK;
1021 }
1022
1023 uint32_t poll_timeout_ms = 0u;
1024 auto status = backend_.DfuDownload(current_alt_setting_, pending_block_num_, data,
1025 poll_timeout_ms);
1027 poll_timeout_ms_ = poll_timeout_ms;
1028
1029 if (status == DFUStatusCode::OK)
1030 {
1031 state_ = DFUState::DFU_DNLOAD_SYNC;
1032 status_ = DFUStatusCode::OK;
1033 download_started_ = true;
1034 }
1035 else
1036 {
1037 EnterErrorState(status);
1038 }
1039 return ErrorCode::OK;
1040 }
1041
1042 void OnClassInDataStatusComplete(bool, uint8_t bRequest) override
1043 {
1044 if (static_cast<DFURequest>(bRequest) != DFURequest::GETSTATUS)
1045 {
1046 return;
1047 }
1048 if (state_ == DFUState::DFU_MANIFEST_WAIT_RESET)
1049 {
1050 // 只有在主机完成了报告 MANIFEST-WAIT-RESET 的那笔 GETSTATUS 的 STATUS OUT 后,
1051 // 才真正提交 autorun。
1052 // Commit autorun only after the host completes the STATUS OUT of the
1053 // GETSTATUS request that reported MANIFEST-WAIT-RESET.
1054 backend_.DfuCommitManifestWaitReset(current_alt_setting_);
1055 }
1056 }
1057
1058 private:
1059 static constexpr uint8_t BuildAttributeBitmap(const DFUCapabilities& caps)
1060 {
1061 return static_cast<uint8_t>(
1062 (caps.can_download ? ATTR_CAN_DOWNLOAD : 0u) |
1063 (caps.can_upload ? ATTR_CAN_UPLOAD : 0u) |
1064 (caps.manifestation_tolerant ? ATTR_MANIFESTATION_TOLERANT : 0u) |
1065 (caps.will_detach ? ATTR_WILL_DETACH : 0u));
1066 }
1067
1068 ErrorCode HandleDetach(ControlTransferResult&)
1069 {
1070 // 固件态 DFU 不实现 DETACH;该请求由 runtime DFU 处理。
1071 // Firmware-mode DFU does not implement DETACH; runtime DFU owns that request.
1072 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1073 }
1074
1075 ErrorCode HandleDnload(uint16_t block_num, uint16_t wLength,
1076 ControlTransferResult& result)
1077 {
1078 if (!caps_.can_download)
1079 {
1080 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1081 }
1082 if (!(state_ == DFUState::DFU_IDLE || state_ == DFUState::DFU_DNLOAD_IDLE))
1083 {
1084 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1085 }
1086 if (wLength > caps_.transfer_size)
1087 {
1088 return ProtocolStall(DFUStatusCode::ERR_USBR);
1089 }
1090
1091 if (wLength == 0u)
1092 {
1093 // 零长度 DNLOAD 表示结束数据阶段并进入 manifest,
1094 // 但前提是之前至少成功接收过一个 payload block。
1095 // A zero-length DNLOAD terminates the data phase and requests manifest,
1096 // but only after at least one payload block was accepted.
1098 pending_block_num_ = block_num;
1099 if (!download_started_)
1100 {
1101 EnterErrorState(DFUStatusCode::ERR_NOTDONE);
1102 }
1103 else
1104 {
1105 state_ = DFUState::DFU_MANIFEST_SYNC;
1106 status_ = DFUStatusCode::OK;
1107 poll_timeout_ms_ = 0u;
1108 }
1109 result.SendStatusInZLP() = true;
1110 return ErrorCode::OK;
1111 }
1112
1113 pending_block_num_ = block_num;
1114 pending_dnload_length_ = wLength;
1115 result.OutData() = {transfer_buffer_, wLength};
1116 return ErrorCode::OK;
1117 }
1118
1119 ErrorCode HandleUpload(uint16_t block_num, uint16_t wLength,
1120 ControlTransferResult& result)
1121 {
1122 if (!caps_.can_upload)
1123 {
1124 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1125 }
1126 if (!(state_ == DFUState::DFU_IDLE || state_ == DFUState::DFU_UPLOAD_IDLE))
1127 {
1128 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1129 }
1130 if (wLength == 0u)
1131 {
1132 return ProtocolStall(DFUStatusCode::ERR_USBR);
1133 }
1134
1135 size_t req_size = wLength;
1136 if (req_size > caps_.transfer_size)
1137 {
1138 req_size = caps_.transfer_size;
1139 }
1140
1141 DFUStatusCode op_status = DFUStatusCode::OK;
1142 uint32_t poll_timeout_ms = 0u;
1143 const size_t read_size =
1144 backend_.DfuUpload(current_alt_setting_, block_num, {transfer_buffer_, req_size},
1145 op_status, poll_timeout_ms);
1146
1147 poll_timeout_ms_ = poll_timeout_ms;
1148 if (op_status != DFUStatusCode::OK)
1149 {
1150 return ProtocolStall(op_status);
1151 }
1152 if (read_size > req_size)
1153 {
1154 return ProtocolStall(DFUStatusCode::ERR_USBR);
1155 }
1156
1157 status_ = DFUStatusCode::OK;
1158 state_ = (read_size < req_size) ? DFUState::DFU_IDLE : DFUState::DFU_UPLOAD_IDLE;
1159 result.InData() = {transfer_buffer_, read_size};
1160 return ErrorCode::OK;
1161 }
1162
1163 ErrorCode HandleGetStatus(ControlTransferResult& result)
1164 {
1165 AdvanceStateForStatusRead();
1166 status_response_.bStatus = static_cast<uint8_t>(status_);
1167 status_response_.SetPollTimeout(poll_timeout_ms_);
1168 status_response_.bState = static_cast<uint8_t>(state_);
1169 status_response_.iString = 0u;
1170 result.InData() = {reinterpret_cast<const uint8_t*>(&status_response_),
1171 sizeof(status_response_)};
1172 return ErrorCode::OK;
1173 }
1174
1175 ErrorCode HandleClearStatus(ControlTransferResult& result)
1176 {
1177 if (state_ != DFUState::DFU_ERROR)
1178 {
1179 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1180 }
1181 backend_.DfuClearStatus(current_alt_setting_);
1182 ClearErrorState();
1183 result.SendStatusInZLP() = true;
1184 return ErrorCode::OK;
1185 }
1186
1187 ErrorCode HandleGetState(ControlTransferResult& result)
1188 {
1189 state_response_ = static_cast<uint8_t>(state_);
1190 result.InData() = {&state_response_, sizeof(state_response_)};
1191 return ErrorCode::OK;
1192 }
1193
1194 ErrorCode HandleAbort(ControlTransferResult& result)
1195 {
1196 switch (state_)
1197 {
1198 case DFUState::DFU_IDLE:
1199 case DFUState::DFU_DNLOAD_SYNC:
1200 case DFUState::DFU_DNLOAD_IDLE:
1201 case DFUState::DFU_UPLOAD_IDLE:
1202 case DFUState::DFU_MANIFEST_SYNC:
1203 backend_.DfuAbort(current_alt_setting_);
1204 ClearErrorState();
1205 result.SendStatusInZLP() = true;
1206 return ErrorCode::OK;
1207
1208 default:
1209 return ProtocolStall(DFUStatusCode::ERR_STALLEDPKT);
1210 }
1211 }
1212
1213 void AdvanceStateForStatusRead()
1214 {
1215 // 标准 DFU 状态机是在 GETSTATUS 这个同步点上推进的。
1216 // GETSTATUS is the synchronization point that advances the standard DFU state
1217 // machine.
1218 switch (state_)
1219 {
1220 case DFUState::DFU_DNLOAD_SYNC:
1221 RefreshDownloadStatus();
1222 break;
1223
1224 case DFUState::DFU_MANIFEST_SYNC:
1225 {
1226 if (status_ != DFUStatusCode::OK)
1227 {
1228 state_ = DFUState::DFU_ERROR;
1229 break;
1230 }
1231
1232 uint32_t poll_timeout_ms = 0u;
1233 auto manifest_status =
1234 backend_.DfuManifest(current_alt_setting_, poll_timeout_ms);
1235 poll_timeout_ms_ = poll_timeout_ms;
1236 if (manifest_status == DFUStatusCode::OK)
1237 {
1238 status_ = DFUStatusCode::OK;
1239 state_ = DFUState::DFU_MANIFEST;
1240 }
1241 else
1242 {
1243 EnterErrorState(manifest_status);
1244 }
1245 break;
1246 }
1247
1248 case DFUState::DFU_DNBUSY:
1249 RefreshDownloadStatus();
1250 break;
1251
1252 case DFUState::DFU_MANIFEST:
1253 RefreshManifestStatus();
1254 break;
1255
1256 default:
1257 break;
1258 }
1259 }
1260
1261 void RefreshDownloadStatus()
1262 {
1263 if (status_ != DFUStatusCode::OK)
1264 {
1265 state_ = DFUState::DFU_ERROR;
1266 return;
1267 }
1268
1269 bool busy = false;
1270 uint32_t poll_timeout_ms = 0u;
1271 const auto download_status =
1272 backend_.DfuGetDownloadStatus(current_alt_setting_, busy, poll_timeout_ms);
1273 poll_timeout_ms_ = poll_timeout_ms;
1274 if (download_status != DFUStatusCode::OK)
1275 {
1276 EnterErrorState(download_status);
1277 return;
1278 }
1279
1280 // backend 报 busy 时映射到 DFU_DNBUSY;
1281 // 否则主机可以从 DFU_DNLOAD_IDLE 继续发送下一块 DNLOAD。
1282 // A backend-reported busy maps to DFU_DNBUSY; otherwise the host may
1283 // continue sending the next DNLOAD block from DFU_DNLOAD_IDLE.
1284 state_ = busy ? DFUState::DFU_DNBUSY : DFUState::DFU_DNLOAD_IDLE;
1285 }
1286
1287 void RefreshManifestStatus()
1288 {
1289 if (status_ != DFUStatusCode::OK)
1290 {
1291 state_ = DFUState::DFU_ERROR;
1292 return;
1293 }
1294
1295 bool busy = false;
1296 uint32_t poll_timeout_ms = 0u;
1297 const auto manifest_status =
1298 backend_.DfuGetManifestStatus(current_alt_setting_, busy, poll_timeout_ms);
1299 poll_timeout_ms_ = poll_timeout_ms;
1300 if (manifest_status != DFUStatusCode::OK)
1301 {
1302 EnterErrorState(manifest_status);
1303 return;
1304 }
1305
1306 if (busy)
1307 {
1308 state_ = DFUState::DFU_MANIFEST;
1309 return;
1310 }
1311
1312 // manifest 完成后,要么回到 IDLE(tolerant),要么进入 WAIT_RESET(non-tolerant)。
1313 // Manifest completion returns either to IDLE (tolerant) or WAIT_RESET (non-tolerant).
1314 download_started_ = false;
1315 state_ = caps_.manifestation_tolerant ? DFUState::DFU_IDLE
1316 : DFUState::DFU_MANIFEST_WAIT_RESET;
1317 }
1318
1319 void ResetProtocolState()
1320 {
1321 // 这里只重置前端拥有的协议状态;
1322 // 镜像级 bookkeeping 保留在 backend_ 里。
1323 // Reset only frontend-owned protocol state;
1324 // image-level bookkeeping stays in backend_.
1325 pending_block_num_ = 0u;
1327 poll_timeout_ms_ = 0u;
1328 current_alt_setting_ = 0u;
1329 download_started_ = false;
1330 ClearErrorState();
1331 }
1332
1333 void ClearErrorState()
1334 {
1335 status_ = DFUStatusCode::OK;
1336 state_ = DFUState::DFU_IDLE;
1337 poll_timeout_ms_ = 0u;
1338 }
1339
1340 void EnterErrorState(DFUStatusCode status)
1341 {
1342 // 一旦锁存错误,前端会保持在 DFU_ERROR,
1343 // 直到 CLRSTATUS/ABORT 把它清掉。
1344 // Once an error is latched, the frontend stays in DFU_ERROR
1345 // until CLRSTATUS/ABORT clears it.
1346 status_ = status;
1347 state_ = DFUState::DFU_ERROR;
1348 poll_timeout_ms_ = 0u;
1349 }
1350
1351 ErrorCode ProtocolStall(DFUStatusCode status)
1352 {
1353 EnterErrorState(status);
1354 return ErrorCode::ARG_ERR;
1355 }
1356
1357 private:
1358 Backend& backend_;
1362 uint8_t state_response_ = 0u;
1363 uint8_t transfer_buffer_[MAX_TRANSFER_SIZE] =
1364 {};
1365 bool download_started_ = false;
1366 uint16_t pending_block_num_ = 0u;
1368 uint32_t poll_timeout_ms_ = 0u;
1369 DFUState state_ = DFUState::DFU_IDLE;
1370 DFUStatusCode status_ = DFUStatusCode::OK;
1371};
1372
1378{
1379 protected:
1380 using JumpCallback = DfuBootloaderBackend::JumpCallback;
1381
1382 DfuBootloaderClassStorage(Flash& flash, size_t image_base, size_t image_limit,
1383 size_t seal_offset, JumpCallback jump_to_app,
1384 void* jump_app_ctx = nullptr, bool autorun = true)
1385 : backend_(flash, image_base, image_limit, seal_offset, jump_to_app, jump_app_ctx,
1386 autorun),
1387 image_base_(image_base),
1388 image_limit_(image_limit),
1389 seal_offset_(seal_offset)
1390 {
1391 }
1392
1393 DfuBootloaderBackend backend_;
1394 size_t image_base_ = 0u;
1395 size_t image_limit_ = 0u;
1396 size_t seal_offset_ = 0u;
1397};
1398
1403template <size_t MAX_TRANSFER_SIZE = 4096u>
1405 public DFUClass<DfuBootloaderBackend, MAX_TRANSFER_SIZE>
1406{
1409
1410 public:
1411 using JumpCallback = DfuBootloaderBackend::JumpCallback;
1412 static constexpr uint8_t VENDOR_REQUEST_RUN_APP = 0x5Au;
1413
1415 Flash& flash, size_t image_base, size_t image_limit, size_t seal_offset,
1416 JumpCallback jump_to_app, void* jump_app_ctx = nullptr, bool autorun = true,
1417 const char* interface_string = Base::DEFAULT_INTERFACE_STRING,
1418 const char* webusb_landing_page_url = nullptr,
1419 uint8_t webusb_vendor_code = LibXR::USB::WebUsb::WEBUSB_VENDOR_CODE_DEFAULT,
1420 const char* winusb_device_interface_guid =
1421 Base::DEFAULT_WINUSB_DEVICE_INTERFACE_GUID,
1422 uint8_t winusb_vendor_code = Base::DEFAULT_WINUSB_VENDOR_CODE)
1423 : Storage(flash, image_base, image_limit, seal_offset, jump_to_app, jump_app_ctx,
1424 autorun),
1425 Base(Storage::backend_, interface_string, webusb_landing_page_url,
1426 webusb_vendor_code, winusb_device_interface_guid, winusb_vendor_code)
1427 {
1428 }
1429
1430 // 这里只推进 backend 拥有的异步工作;真正跳 app 仍保持显式调用。
1431 // Process only backend-owned async work; actual app launch remains explicit.
1432 void Process() { Storage::backend_.Process(); }
1433 bool RequestRunApp() { return Storage::backend_.TryRequestRunApp(); }
1434
1435 bool TryConsumeAppLaunch(uint32_t now_ms)
1436 {
1437 return Storage::backend_.TryConsumeAppLaunch(now_ms);
1438 }
1439
1440 bool HasPendingWork() const { return Storage::backend_.HasPendingWork(); }
1441 bool HasValidImage() const { return Storage::backend_.HasValidImage(); }
1442 size_t ImageSize() const { return Storage::backend_.ImageSize(); }
1443 size_t ImageBase() const { return Storage::image_base_; }
1444 size_t ImageLimit() const { return Storage::image_limit_; }
1445 size_t SealOffset() const { return Storage::seal_offset_; }
1446
1447 protected:
1448 ErrorCode OnVendorRequest(bool, uint8_t bRequest, uint16_t wValue, uint16_t wLength,
1449 uint16_t,
1450 typename Base::ControlTransferResult& result) override
1451 {
1452 if (bRequest != VENDOR_REQUEST_RUN_APP)
1453 {
1455 }
1456 if (wLength != 0u || wValue != 0u)
1457 {
1458 return ErrorCode::ARG_ERR;
1459 }
1460 if (Storage::backend_.HasPendingWork())
1461 {
1462 // 协议拥有的异步工作尚未完成时,不允许启动 app。
1463 // Do not launch the app while protocol-owned async work is still pending.
1464 return ErrorCode::BUSY;
1465 }
1466 if (!Storage::backend_.TryRequestRunApp())
1467 {
1468 // RUN_APP 只在已知存在有效 seal 镜像时才会接受。
1469 // RUN_APP is only accepted when a valid sealed image is known.
1470 return ErrorCode::FAILED;
1471 }
1472 result.SendStatusInZLP() = true;
1473 return ErrorCode::OK;
1474 }
1475};
1476
1477using DfuBootloaderClass = DfuBootloaderClassT<4096u>;
1478
1479} // namespace LibXR::USB
static bool inited_
查找表是否已初始化 / Whether the lookup table is initialized
Definition crc.hpp:220
static uint32_t tab_[256]
CRC32 查找表 / CRC32 lookup table.
Definition crc.hpp:219
static void GenerateTable()
生成 CRC32 查找表 / Generates the CRC32 lookup table
Definition crc.hpp:228
只读原始数据视图 / 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:19
size_t MinWriteSize() const
Returns the minimum writable block size in bytes. 获取最小可写块大小(字节)。
Definition flash.hpp:73
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
可写原始数据视图 / Mutable raw data view
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.
DFUStatusCode status_
DFU 状态码 / DFU status code.
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).
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:87
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: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: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