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 = 2048bytes per descriptordma_half_buffer_size = 4096bytes per EOFsizeof(dma_elem_t) = 4- →
rx_eof_num = 1024samples 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_taskadvances 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 validcamera_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 deviceslibqemu-xtensa.dll.new— intermediate test buildlibqemu-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
- esp32-camera/target/esp32/ll_cam.c::ll_cam_start — rx_eof_num setup
- esp32-camera/driver/cam_hal.c::cam_task — state machine
- esp32-camera/driver/cam_hal.c::allocate_dma_descriptors — descriptor sizing