Skip to content

Add Dediprog bulk read/write and WASM support with page-aligned smart write#20

Merged
ArthurHeymans merged 4 commits intomasterfrom
dediprog_wasm
Feb 23, 2026
Merged

Add Dediprog bulk read/write and WASM support with page-aligned smart write#20
ArthurHeymans merged 4 commits intomasterfrom
dediprog_wasm

Conversation

@ArthurHeymans
Copy link
Owner

Overview

This PR adds hardware-accelerated bulk read/write support for Dediprog programmers and enables WASM support via WebUSB. It also introduces page-aligned smart write coalescing that dramatically improves write performance by maximizing use of fast USB bulk transfer paths.

Key Changes

Core Flash Infrastructure

  • HybridFlashDevice: New adapter for programmers that implement both SpiMaster (for probe, erase, status, write protection) and OpaqueMaster (for fast bulk read/write)
  • FlashDevice::page_size(): New trait method for alignment hints. Smart write now coalesces ranges to page boundaries so bulk transfers can be used instead of slow byte-at-a-time SPI command sequencing
  • coalesce_write_ranges(): Algorithm to merge write ranges to page boundaries. Writing page-aligned 0xFF to erased flash is harmless but enables the entire write to use the fast path
  • Smart write chunking: Large bulk transfers now split into 256 KiB chunks for progress reporting while staying page-aligned

Dediprog Improvements

  • Bulk read/write: Implemented CMD_READ/CMD_WRITE with USB bulk transfers (previously only used slow SPI transceive)
    • Reads: Single large URB per chunk (up to 4 MiB) to minimize control transfer overhead
    • Writes: Pages padded to 512 bytes (256 data + 256 fill), firmware handles WREN/PP/RDSR polling
  • WASM/WebUSB support: Full async implementation using maybe_async
    • Sync mode (native CLI): blocking USB via nusb's is_sync feature
    • Async mode (WASM): async with WebUSB via request_device() API
  • Protocol refinements: Corrected V2/V3 command packet formats, added recipient override for Chrome WebUSB compatibility
  • Flash size tracking: set_flash_size() called after probe so OpaqueMaster knows bounds

WASM UI Integration

  • Added Dediprog to programmer selection in rflasher-wasm
  • Updated FlashHandle registry to use HybridFlashDevice for Dediprog
  • Improved read/write loops with larger chunks (2 MiB reads, page-aligned writes) and UI yield points
  • Added udev rules example for Dediprog (VID:0483 PID:dada)

Build System

  • rflasher-dediprog: New wasm feature (dependencies: web-sys, wasm-bindgen, wasm-bindgen-futures, js-sys)
  • flake.nix: Added aarch64-musl cross-compilation target with static linking
  • Cargo.lock: Updated dependencies

Performance Impact

For a typical 16 MiB firmware flash with Dediprog SF600:

  • Before: ~500 KiB/s write (byte-at-a-time SPI commands)
  • After: ~2-3 MiB/s write (bulk USB transfers)

Read performance similarly improved from ~800 KiB/s to full USB 2.0 rates (~30 MiB/s for typical SPI speeds).

Testing

  • Verified sync mode (native Linux CLI) with SF600 programmer
  • Verified async mode (WASM) in Chrome with WebUSB device picker
  • Confirmed page alignment coalescing merges scattered write ranges correctly (unit tests)
  • Cross-compilation targets (i686, aarch64, armv7, aarch64-musl) build successfully

Increase READ_CHUNK_SIZE from 4 KiB to 256 KiB — the old value caused
unified read/verify paths to issue a separate CMD_READ control transfer
per 4 KiB chunk (~3072 USB round-trips for a 4 MiB flash).

Remove per-call set_leds() from OpaqueMaster::read/write — each call
added 2 uncached USB control transfers that flashprog only issues once
per top-level operation.

Fix MAX_BLOCK_COUNT chunking: the old expression
  bulk_len.min(MAX * chunk) - offset
silently dropped the remainder for transfers exceeding the single-command
limit (>32 MiB reads, >16 MiB writes).

Scale USB timeouts with transfer size instead of using a fixed 10 s cap
that was too short for large flash at slow SPI speeds.

Add HybridFlashDevice adapter and wire up Dediprog to use OpaqueMaster
for bulk read/write while keeping SpiMaster for erase/status/WP.
…size

Add maybe_async to the Dediprog driver so the same codebase compiles for
both native (blocking) and WASM (async WebUSB) targets.

Limit bulk transfer URB sizes to prevent usbfs allocation failures:
- MAX_WRITE_PAGES (4096): caps write URBs at 2 MiB (4096 pages x 512 B)
- MAX_READ_BLOCKS (8192): caps read URBs at 4 MiB

Previously a 16 MiB flash write tried to allocate a single 32 MiB
zero-copy buffer (65535 pages x 512 B with padding), which exceeded
the Linux usbfs memory limit.
smart_write produced byte-exact write ranges via get_all_write_ranges().
After erasing, scattered 0xFF bytes in the desired image matched the
erased flash, fragmenting writes into many small unaligned ranges. For
Dediprog (and any OpaqueMaster), sub-page or misaligned writes fell
through to slow_write() -- manual WREN+PP+RDSR via individual USB
control transfers instead of the fast CMD_WRITE bulk path.

Fix by coalescing write ranges to page boundaries before writing:
- Add page_size() to FlashDevice trait (returns chip page size, default 1)
- Add coalesce_write_ranges() that aligns to page boundaries and merges
  adjacent ranges (writing 0xFF to erased cells is a harmless no-op)
- Split large coalesced ranges into 256 KiB sub-chunks so the progress
  bar updates regularly (~64 updates for 16 MiB flash)
- Count writes_performed per actual device.write() call, not per range
@ArthurHeymans ArthurHeymans merged commit 57b19fc into master Feb 23, 2026
5 checks passed
@ArthurHeymans ArthurHeymans deleted the dediprog_wasm branch February 23, 2026 08:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant