Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion applications/main/subghz/scenes/subghz_scene_save_name.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ void subghz_scene_save_name_on_enter(void* context) {
furi_string_set(subghz->file_path, dir_name);
}

strncpy(subghz->file_name_tmp, furi_string_get_cstr(file_name), SUBGHZ_MAX_LEN_NAME);
strlcpy(subghz->file_name_tmp, furi_string_get_cstr(file_name), SUBGHZ_MAX_LEN_NAME);
text_input_set_header_text(text_input, "Name signal");
text_input_set_result_callback(
text_input,
Expand Down
30 changes: 15 additions & 15 deletions furi/core/check.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ FURI_NORETURN void __furi_halt_implementation(void);
#define furi_halt(...) M_APPLY(__furi_halt, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__)))

/** Check condition and crash if check failed */
#define __furi_check(__e, __m) \
do { \
if(!(__e)) { \
__furi_crash(__m); \
} \
#define __furi_check(__e, __m) \
do { \
if(__builtin_expect(!(__e), 0)) { \
__furi_crash(__m); \
} \
} while(0)

/** Check condition and crash if failed
Expand All @@ -75,11 +75,11 @@ FURI_NORETURN void __furi_halt_implementation(void);

/** Only in debug build: Assert condition and crash if assert failed */
#ifdef FURI_DEBUG
#define __furi_assert(__e, __m) \
do { \
if(!(__e)) { \
__furi_crash(__m); \
} \
#define __furi_assert(__e, __m) \
do { \
if(__builtin_expect(!(__e), 0)) { \
__furi_crash(__m); \
} \
} while(0)
#else
#define __furi_assert(__e, __m) \
Expand All @@ -98,11 +98,11 @@ FURI_NORETURN void __furi_halt_implementation(void);
#define furi_assert(...) \
M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__))

#define furi_break(__e) \
do { \
if(!(__e)) { \
asm volatile("bkpt 0"); \
} \
#define furi_break(__e) \
do { \
if(__builtin_expect(!(__e), 0)) { \
asm volatile("bkpt 0"); \
} \
} while(0)

#ifdef __cplusplus
Expand Down
2 changes: 1 addition & 1 deletion furi/core/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ FuriStatus furi_delay_until_tick(uint32_t tick) {
return stat;
}

uint32_t furi_get_tick(void) {
__attribute__((flatten)) uint32_t furi_get_tick(void) {
TickType_t ticks;

if(furi_kernel_is_irq_or_masked() != 0U) {
Expand Down
5 changes: 4 additions & 1 deletion furi/core/memmgr.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "memmgr.h"
#include "memmgr_heap.h"
#include <string.h>
#include <furi_hal_memory.h>
#include <FreeRTOS.h>
Expand All @@ -25,7 +26,9 @@ void* realloc(void* ptr, size_t size) {

void* p = pvPortMalloc(size);
if(ptr != NULL) {
memcpy(p, ptr, size);
size_t old_size = memmgr_heap_get_block_size(ptr);
size_t copy_size = old_size < size ? old_size : size;
memcpy(p, ptr, copy_size);
vPortFree(ptr);
}

Expand Down
20 changes: 20 additions & 0 deletions furi/core/memmgr_heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,24 @@ size_t xPortGetMinimumEverFreeHeapSize(void) {
}
/*-----------------------------------------------------------*/

size_t memmgr_heap_get_block_size(const void* pv) {
const uint8_t* puc = (const uint8_t*)pv;
BlockLink_t* pxLink;

if(pv == NULL) {
return 0;
}

puc -= xHeapStructSize;
pxLink = (BlockLink_t*)(uintptr_t)puc;

heapVALIDATE_BLOCK_POINTER(pxLink);
configASSERT(heapBLOCK_IS_ALLOCATED(pxLink) != 0);

return (pxLink->xBlockSize & ~heapBLOCK_ALLOCATED_BITMASK) - xHeapStructSize;
}
/*-----------------------------------------------------------*/

void xPortResetHeapMinimumEverFreeHeapSize(void) {
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
}
Expand Down Expand Up @@ -734,3 +752,5 @@ void vPortHeapResetState(void) {
xNumberOfSuccessfulFrees = (size_t)0U;
}
/*-----------------------------------------------------------*/

/*-----------------------------------------------------------*/
7 changes: 7 additions & 0 deletions furi/core/memmgr_heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ size_t memmgr_heap_get_max_free_block(void);
*/
void memmgr_heap_printf_free_blocks(void);

/** Get usable size of an allocated heap block
*
* @param ptr pointer to allocated memory
* @return usable size in bytes
*/
size_t memmgr_heap_get_block_size(const void* ptr);

#ifdef __cplusplus
}
#endif
33 changes: 28 additions & 5 deletions furi/core/string.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "string.h"
#include <m-string.h>
#include <stdio.h>

struct FuriString {
string_t string;
Expand Down Expand Up @@ -175,11 +176,33 @@ int furi_string_cat_printf(FuriString* v, const char format[], ...) {
}

int furi_string_cat_vprintf(FuriString* v, const char format[], va_list args) {
FuriString* string = furi_string_alloc();
int ret = furi_string_vprintf(string, format, args);
furi_string_cat(v, string);
furi_string_free(string);
return ret;
// In-place append: format directly into destination buffer at current offset
// Eliminates temporary string allocation (malloc + free) per call
va_list args_copy;
va_copy(args_copy, args);

size_t old_size = string_size(v->string);
char* ptr = m_str1ng_get_cstr(v->string);
size_t alloc = string_capacity(v->string);

int size = vsnprintf(&ptr[old_size], alloc - old_size, format, args);

if(size > 0 && (old_size + (size_t)size + 1 > alloc)) {
// Buffer too small — grow and retry
ptr = m_str1ng_fit2size(v->string, old_size + (size_t)size + 1);
size = vsnprintf(
&ptr[old_size], string_capacity(v->string) - old_size, format, args_copy);
}
va_end(args_copy);

if(size >= 0) {
m_str1ng_set_size(v->string, old_size + (size_t)size);
} else {
// vsnprintf error — restore original string
ptr[old_size] = 0;
}

return size;
}

bool furi_string_empty(const FuriString* v) {
Expand Down
6 changes: 3 additions & 3 deletions furi/core/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -458,11 +458,11 @@ int32_t furi_thread_get_return_code(FuriThread* thread) {
return thread->ret;
}

FuriThreadId furi_thread_get_current_id(void) {
__attribute__((flatten)) FuriThreadId furi_thread_get_current_id(void) {
return (FuriThreadId)xTaskGetCurrentTaskHandle();
}

FuriThread* furi_thread_get_current(void) {
__attribute__((flatten)) FuriThread* furi_thread_get_current(void) {
FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0);
return thread;
}
Expand Down Expand Up @@ -538,7 +538,7 @@ uint32_t furi_thread_flags_clear(uint32_t flags) {
return rflags;
}

uint32_t furi_thread_flags_get(void) {
__attribute__((flatten)) uint32_t furi_thread_flags_get(void) {
TaskHandle_t hTask;
uint32_t rflags;

Expand Down
2 changes: 1 addition & 1 deletion site_scons/firmwareopts.scons
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ else:
"NDEBUG",
],
CCFLAGS=[
"-Og",
"-Os",
],
)

Expand Down
3 changes: 2 additions & 1 deletion targets/f7/api_symbols.csv
Original file line number Diff line number Diff line change
Expand Up @@ -1785,7 +1785,7 @@ Function,+,furi_hal_spi_bus_handle_init,void,const FuriHalSpiBusHandle*
Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus*
Function,+,furi_hal_spi_bus_rx,_Bool,"const FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t"
Function,+,furi_hal_spi_bus_trx,_Bool,"const FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t"
Function,+,furi_hal_spi_bus_trx_dma,_Bool,"const FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t"
Function,+,furi_hal_spi_bus_trx_dma,_Bool,"const FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t"
Function,+,furi_hal_spi_bus_tx,_Bool,"const FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t"
Function,-,furi_hal_spi_config_deinit_early,void,
Function,-,furi_hal_spi_config_init,void,
Expand Down Expand Up @@ -2711,6 +2711,7 @@ Function,+,memmgr_get_minimum_free_heap,size_t,
Function,+,memmgr_get_total_heap,size_t,
Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId
Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId
Function,+,memmgr_heap_get_block_size,size_t,const void*
Function,+,memmgr_heap_get_max_free_block,size_t,
Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId
Function,+,memmgr_heap_printf_free_blocks,void,
Expand Down
85 changes: 73 additions & 12 deletions targets/f7/furi_hal/furi_hal_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ bool furi_hal_spi_bus_tx(
furi_check(buffer);
furi_check(size > 0);

if(furi_kernel_is_running()) {
// Use DMA for TX when scheduler is running, freeing the CPU during transfer
bool ret = furi_hal_spi_bus_trx_dma(handle, buffer, NULL, size, timeout);
LL_SPI_ClearFlag_OVR(handle->bus->spi);
return ret;
}

// Polling fallback for pre-scheduler context
bool ret = true;

while(size > 0) {
Expand Down Expand Up @@ -193,7 +201,7 @@ static void spi_dma_isr(void* context) {

bool furi_hal_spi_bus_trx_dma(
const FuriHalSpiBusHandle* handle,
uint8_t* tx_buffer,
const uint8_t* tx_buffer,
uint8_t* rx_buffer,
size_t size,
uint32_t timeout_ms) {
Expand All @@ -203,6 +211,9 @@ bool furi_hal_spi_bus_trx_dma(

// If scheduler is not running, use blocking mode
if(!furi_kernel_is_running()) {
if(rx_buffer == NULL) {
return furi_hal_spi_bus_tx(handle, tx_buffer, size, timeout_ms);
}
return furi_hal_spi_bus_trx(handle, tx_buffer, rx_buffer, size, timeout_ms);
}

Expand All @@ -227,9 +238,26 @@ bool furi_hal_spi_bus_trx_dma(
}

if(rx_buffer == NULL) {
// Only TX mode, do not use RX channel
// TX-only mode: set up RX DMA to drain incoming bytes (prevents OVR)
uint8_t dma_rx_dummy;

LL_DMA_InitTypeDef dma_config = {0};

// RX DMA channel: drain SPI RX FIFO into dummy byte (no increment)
dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR);
dma_config.MemoryOrM2MDstAddress = (uint32_t)&dma_rx_dummy;
dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
dma_config.Mode = LL_DMA_MODE_NORMAL;
dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_NOINCREMENT;
dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
dma_config.NbData = size;
dma_config.PeriphRequest = dma_rx_req;
dma_config.Priority = LL_DMA_PRIORITY_MEDIUM;
LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config);

// TX DMA channel
dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR);
dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer;
dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
Expand All @@ -243,48 +271,70 @@ bool furi_hal_spi_bus_trx_dma(
dma_config.Priority = LL_DMA_PRIORITY_MEDIUM;
LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config);

#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7
#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 && SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7
LL_DMA_ClearFlag_TC6(SPI_DMA);
LL_DMA_ClearFlag_TC7(SPI_DMA);
#else
#error Update this code. Would you kindly?
#endif

furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, spi_dma_isr, NULL);
furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, spi_dma_isr, NULL);

bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi);
bool dma_rx_was_enabled = LL_SPI_IsEnabledDMAReq_RX(spi);
if(!dma_tx_was_enabled) {
LL_SPI_EnableDMAReq_TX(spi);
}
if(!dma_rx_was_enabled) {
LL_SPI_EnableDMAReq_RX(spi);
}

// acquire semaphore before enabling DMA
furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk);

LL_DMA_EnableIT_TC(SPI_DMA_TX_DEF);
LL_DMA_EnableIT_TC(SPI_DMA_RX_DEF);
LL_DMA_EnableChannel(SPI_DMA_RX_DEF);
LL_DMA_EnableChannel(SPI_DMA_TX_DEF);

// and wait for it to be released (DMA transfer complete)
// wait for RX DMA complete (all bytes transmitted and drained)
if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) {
ret = false;
FURI_LOG_E(TAG, "DMA timeout\r\n");
}

// Disable TC IRQ and clear pending TC flag BEFORE releasing the
// semaphore. On timeout the ISR may still fire and would try to
// re-release the binary semaphore, crashing furi_check.
LL_DMA_DisableIT_TC(SPI_DMA_RX_DEF);
#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 && SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7
LL_DMA_ClearFlag_TC6(SPI_DMA);
LL_DMA_ClearFlag_TC7(SPI_DMA);
#else
#error Update this code. Would you kindly?
#endif

// release semaphore, because we are using it as a flag
furi_semaphore_release(spi_dma_completed);

LL_DMA_DisableIT_TC(SPI_DMA_TX_DEF);
LL_DMA_DisableChannel(SPI_DMA_TX_DEF);
LL_DMA_DisableChannel(SPI_DMA_RX_DEF);
if(!dma_tx_was_enabled) {
LL_SPI_DisableDMAReq_TX(spi);
}
furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, NULL, NULL);
if(!dma_rx_was_enabled) {
LL_SPI_DisableDMAReq_RX(spi);
}
furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, NULL, NULL);

LL_DMA_DeInit(SPI_DMA_TX_DEF);
LL_DMA_DeInit(SPI_DMA_RX_DEF);
} else {
// TRX or RX mode, use both channels
uint32_t tx_mem_increase_mode;

if(tx_buffer == NULL) {
// RX mode, use dummy data instead of TX buffer
tx_buffer = (uint8_t*)&dma_dummy_u32;
tx_buffer = (const uint8_t*)&dma_dummy_u32;
tx_mem_increase_mode = LL_DMA_MEMORY_NOINCREMENT;
} else {
tx_mem_increase_mode = LL_DMA_MEMORY_INCREMENT;
Expand Down Expand Up @@ -317,8 +367,9 @@ bool furi_hal_spi_bus_trx_dma(
dma_config.Priority = LL_DMA_PRIORITY_MEDIUM;
LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config);

#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6
#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 && SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7
LL_DMA_ClearFlag_TC6(SPI_DMA);
LL_DMA_ClearFlag_TC7(SPI_DMA);
#else
#error Update this code. Would you kindly?
#endif
Expand Down Expand Up @@ -348,10 +399,20 @@ bool furi_hal_spi_bus_trx_dma(
ret = false;
FURI_LOG_E(TAG, "DMA timeout\r\n");
}
// release semaphore, because we are using it as a flag
furi_semaphore_release(spi_dma_completed);

// Disable TC IRQ and clear pending TC flag BEFORE releasing the
// semaphore. On timeout the ISR may still fire and would try to
// re-release the binary semaphore, crashing furi_check.
LL_DMA_DisableIT_TC(SPI_DMA_RX_DEF);
#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 && SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7
LL_DMA_ClearFlag_TC6(SPI_DMA);
LL_DMA_ClearFlag_TC7(SPI_DMA);
#else
#error Update this code. Would you kindly?
#endif

// release semaphore, because we are using it as a flag
furi_semaphore_release(spi_dma_completed);

LL_DMA_DisableChannel(SPI_DMA_TX_DEF);
LL_DMA_DisableChannel(SPI_DMA_RX_DEF);
Expand Down
Loading