Skip to content
Open
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
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
16 changes: 13 additions & 3 deletions 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,15 +26,24 @@ void* realloc(void* ptr, size_t size) {

void* p = pvPortMalloc(size);
if(ptr != NULL) {
memcpy(p, ptr, size);
vPortFree(ptr);
if(p != NULL) {
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);
}
}

return p;
}

void* calloc(size_t count, size_t size) {
return pvPortMalloc(count * size);
size_t total = count * size;
void* p = pvPortMalloc(total);
if(p != NULL) {
memset(p, 0, total);
}
return p;
}

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

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

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

puc -= xHeapStructSize;
pxLink = (void*)puc;

configASSERT(heapBLOCK_IS_ALLOCATED(pxLink) != 0);

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

void xPortResetHeapMinimumEverFreeHeapSize(void) {
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
}
Expand Down
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(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
@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,87.1,,
Version,+,87.2,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
Expand Down Expand Up @@ -2607,6 +2607,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,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
51 changes: 43 additions & 8 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, (uint8_t*)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 @@ -227,9 +235,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,41 +268,51 @@ 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
LL_DMA_ClearFlag_TC7(SPI_DMA);
#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6
LL_DMA_ClearFlag_TC6(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");
}
// 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_DisableIT_TC(SPI_DMA_RX_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;
Expand Down
8 changes: 6 additions & 2 deletions targets/f7/inc/FreeRTOSConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configENABLE_HEAP_PROTECTOR 1
#define configHEAP_CLEAR_MEMORY_ON_FREE 1
#ifdef FURI_DEBUG
#define configHEAP_CLEAR_MEMORY_ON_FREE 1
#else
#define configHEAP_CLEAR_MEMORY_ON_FREE 0
#endif
#define configUSE_MALLOC_FAILED_HOOK 0
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
Expand Down Expand Up @@ -56,7 +60,7 @@
if lengths will always be less than the number of bytes in a size_t. */
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 1
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 4
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
Expand Down