Skip to content

Commit ab36a5d

Browse files
committed
fix jpeg soi check for psram_mode (DMA)
- Added a configurable probe length for PSRAM JPEG validation via CAM_SOI_PROBE_BYTES - For PSRAM mode, CAM_SOI_PROBE_BYTES is copied from the first DMA block into a stack buffer and validated by cam_verify_jpeg_soi() to verify that it's a valid jpeg image before continuing the capture. - Reading from PSRAM directly with cam_verify_jpeg_soi() is here avoided due to latency of small operations done by cam_verify_jpeg_soi().
1 parent 2368d94 commit ab36a5d

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
7575
)
7676
endif()
7777

78-
set(priv_requires freertos nvs_flash)
78+
set(priv_requires freertos nvs_flash esp_mm)
7979

8080
set(min_version_for_esp_timer "4.2")
8181
if (idf_version VERSION_GREATER_EQUAL min_version_for_esp_timer)

driver/cam_hal.c

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
#include "rom/ets_sys.h"
2525
#else
2626
#include "esp_timer.h"
27+
#include "esp_cache.h"
28+
#include "hal/cache_hal.h"
29+
#include "hal/cache_ll.h"
30+
#include "esp_idf_version.h"
2731
#if CONFIG_IDF_TARGET_ESP32
2832
#include "esp32/rom/ets_sys.h" // will be removed in idf v5.0
2933
#elif CONFIG_IDF_TARGET_ESP32S2
@@ -56,6 +60,26 @@ static portMUX_TYPE g_psram_dma_lock = portMUX_INITIALIZER_UNLOCKED;
5660
#define CAM_LOG_SPAM_EVERY_FRAME 0 /* set to 1 to restore old behaviour */
5761
#endif
5862

63+
/* Number of bytes copied to SRAM for SOI validation when capturing
64+
* directly to PSRAM. Tunable to probe more of the frame start if needed. */
65+
#ifndef CAM_SOI_PROBE_BYTES
66+
#define CAM_SOI_PROBE_BYTES 32
67+
#endif
68+
69+
/*
70+
* PSRAM DMA may bypass the CPU cache. Always call esp_cache_msync() on the
71+
* SOI probe region so cached reads see the data written by DMA.
72+
*/
73+
74+
static inline size_t dcache_line_size(void)
75+
{
76+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
77+
return cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
78+
#else
79+
return cache_hal_get_cache_line_size(CACHE_TYPE_DATA);
80+
#endif
81+
}
82+
5983
/* Throttle repeated warnings printed from tight loops / ISRs.
6084
*
6185
* counter – static DRAM/IRAM uint16_t you pass in
@@ -209,11 +233,53 @@ static void cam_task(void *arg)
209233
&cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
210234
cam_obj->dma_half_buffer_size);
211235
}
236+
212237
//Check for JPEG SOI in the first buffer. stop if not found
213-
if (cam_obj->jpeg_mode && cnt == 0 && cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len) != 0) {
214-
ll_cam_stop(cam_obj);
215-
cam_obj->state = CAM_STATE_IDLE;
238+
if (cam_obj->jpeg_mode && cnt == 0) {
239+
if (cam_obj->psram_mode) {
240+
/* dma_half_buffer_size already in BYTES (see ll_cam_memcpy()) */
241+
size_t probe_len = cam_obj->dma_half_buffer_size;
242+
/* clamp to avoid copying past the end of soi_probe */
243+
if (probe_len > CAM_SOI_PROBE_BYTES) {
244+
probe_len = CAM_SOI_PROBE_BYTES;
245+
}
246+
/* Invalidate cache lines for the DMA buffer before probing */
247+
size_t line = dcache_line_size();
248+
if (line == 0) {
249+
line = 32; /* sane fallback */
250+
}
251+
uintptr_t addr = (uintptr_t)frame_buffer_event->buf;
252+
uintptr_t start = addr & ~(line - 1);
253+
size_t sync_len = (probe_len + (addr - start) + line - 1) & ~(line - 1);
254+
esp_cache_msync((void *)start, sync_len,
255+
ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
256+
257+
uint8_t soi_probe[CAM_SOI_PROBE_BYTES];
258+
memcpy(soi_probe, frame_buffer_event->buf, probe_len);
259+
int soi_off = cam_verify_jpeg_soi(soi_probe, probe_len);
260+
if (soi_off != 0) {
261+
static uint16_t warn_psram_soi_cnt = 0;
262+
if (soi_off > 0) {
263+
CAM_WARN_THROTTLE(warn_psram_soi_cnt,
264+
"NO-SOI - JPEG start marker not at pos 0 (PSRAM)");
265+
} else {
266+
CAM_WARN_THROTTLE(warn_psram_soi_cnt,
267+
"NO-SOI - JPEG start marker missing (PSRAM)");
268+
}
269+
ll_cam_stop(cam_obj);
270+
cam_obj->state = CAM_STATE_IDLE;
271+
continue;
272+
}
273+
} else if (cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len) != 0) {
274+
static uint16_t warn_soi_bad_cnt = 0;
275+
CAM_WARN_THROTTLE(warn_soi_bad_cnt,
276+
"NO-SOI - JPEG start marker not at pos 0");
277+
ll_cam_stop(cam_obj);
278+
cam_obj->state = CAM_STATE_IDLE;
279+
continue;
280+
}
216281
}
282+
217283
cnt++;
218284
// stop when too many DMA copies occur so the PSRAM
219285
// framebuffer slot doesn't overflow from runaway transfers

0 commit comments

Comments
 (0)