velxio/test/test-esp32-cam/autosearch/12_descriptor_walker_fix.md

4.9 KiB

12 — Descriptor walker fix + frontend Run integration

Follow-up to 11_blockers_resolved.md. Documents the root cause of esp_camera_fb_get() returning NULL and the multi-descriptor walker fix that should unblock it.

Bug found in walk_dma_chain — single-descriptor fill

Diagnosis

The driver configures the I²S DMA at ll_cam.c:287:

I2S0.rx_eof_num = cam->dma_half_buffer_size / sizeof(dma_elem_t);

With cam_hal's typical defaults for QVGA-JPEG:

  • dma_node_buffer_size = 2048 bytes per descriptor
  • dma_half_buffer_size = 4096 bytes per EOF
  • sizeof(dma_elem_t) = 4
  • rx_eof_num = 1024 samples per EOF

So one EOF spans two descriptors (each holding 512 samples = 2048 bytes). My original walk_dma_chain only filled ONE descriptor before firing EOF — the firmware then saw an EOF with half the expected data, half-buffer pointer indexing went off, and the SOI check on the first chunk silently failed (cam_hal exits READ_BUF state without logging).

This explains why every other piece of the stack tested fine in isolation but fb_get still returned NULL — the gating constraint was the per-EOF sample count, not bit-packing or VSYNC timing.

Fix

hw/misc/esp32_i2s_cam.c::walk_dma_chain rewritten to walk forward through the descriptor ring filling owner=1 descriptors until samples_written >= target_samples. Only the LAST filled descriptor gets eof=1; the others get eof=0 but are still released to CPU (owner=0). The in_suc_eof IRQ fires only after the threshold is crossed.

If the walker hits an owner=0 descriptor before reaching the target, it returns without firing EOF — the next tick continues from where this one left off. This matches real I²S semantics where DMA stalls when no descriptor is available and resumes when CPU releases one.

Reference: esp32-camera/target/esp32/ll_cam.c:287

Verification (pending)

The fix needs the QEMU DLL to be rebuilt and the webcam_demo.ino test re-run. Expected outcome:

  • cam_task advances IDLE → READ_BUF on first VSYNC
  • First EOF arrives with 4096 bytes (1024 samples) of JPEG data
  • SOI check on cnt==0 succeeds (frame starts with 0xFF 0xD8)
  • Subsequent EOFs accumulate
  • Second VSYNC closes the frame and pushes to frame_buffer_queue
  • esp_camera_fb_get() returns a valid camera_fb_t*

Frontend Run-button fix (orthogonal)

EditorToolbar.tsx::handleRun had isQemuBoard checking only the exact strings 'esp32' and 'esp32-s3'. ESP32-CAM ('esp32-cam') fell through to the AVR-style auto-compile path and never called startBoard(). List extended to cover all ESP32 family kinds:

const isQemuBoard =
  board?.boardKind === 'raspberry-pi-3' ||
  board?.boardKind === 'esp32' ||
  board?.boardKind === 'esp32-s3' ||
  board?.boardKind === 'esp32-cam' ||
  board?.boardKind === 'esp32-c3' ||
  board?.boardKind === 'esp32-devkit-c-v4' ||
  board?.boardKind === 'wemos-lolin32-lite' ||
  board?.boardKind === 'xiao-esp32-s3' ||
  board?.boardKind === 'arduino-nano-esp32' ||
  board?.boardKind === 'xiao-esp32-c3' ||
  board?.boardKind === 'aitewinrobot-esp32c3-supermini';

Verified by user: ESP32-CAM now boots in QEMU, runs the webcam_demo, prints SCCB chip-id check, and configures GPIO 25 VSYNC interrupt (Intr:2 = NEGEDGE) — the exact signature cam_hal.c expects.

Build instructions for the DLL

The DLL is built in MSYS2 MINGW64 against the qemu-lcgamboa fork. Path with spaces breaks meson, so we copy to /c/v_qemu/ first:

cp -r "/e/Hardware/velxio release/third-party/qemu-lcgamboa" /c/v_qemu
cd /c/v_qemu
./configure --target-list=xtensa-softmmu --enable-shared
make -j8 lib/libqemu-xtensa.dll
cp build/qemu-system-xtensa /e/Hardware/velxio\ release/backend/app/services/libqemu-xtensa.dll

The build needs the new device sources (esp32_i2s_cam.c, esp32_ov2640.c, velxio_camera_export.c) registered in hw/misc/meson.build and hw/i2c/meson.build — already done.

DLL backup convention

During camera development I kept progressive backups of the working DLL:

  • libqemu-xtensa.dll.pre-camera — last known good before introducing OV2640/I²S devices
  • libqemu-xtensa.dll.new — intermediate test build
  • libqemu-xtensa.dll — current (gitignored, regenerated locally)

The .pre-camera and .new variants are gitignored too — they're local rollback points that shouldn't bloat the repo. If you need a known-good binary, rebuild from the corresponding qemu-lcgamboa commit.

Sources