/*
* This file is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
/*
bouncebuffer code for DMA safe memory operations
*/
#include "stm32_util.h"
#include
#include
#include
#include "bouncebuffer.h"
#if defined(STM32H7)
// always use a bouncebuffer on H7, to ensure alignment and padding
#define IS_DMA_SAFE(addr) false
#elif defined(STM32F732xx)
// always use bounce buffer on F732
#define IS_DMA_SAFE(addr) false
#elif defined(STM32F7)
// on F76x we only consider first half of DTCM memory as DMA safe, 2nd half is used as fast memory for EKF
// on F74x we only have 64k of DTCM
#define IS_DMA_SAFE(addr) ((((uint32_t)(addr)) & ((0xFFFFFFFF & ~(64*1024U-1)) | 1U)) == 0x20000000)
#elif defined(STM32F1)
#define IS_DMA_SAFE(addr) true
#else
// this checks an address is in main memory and 16 bit aligned
#define IS_DMA_SAFE(addr) ((((uint32_t)(addr)) & 0xF0000001) == 0x20000000)
#endif
// Enable when trying to check if you are not just listening yourself
#define ENABLE_ECHO_SAFE 0
/*
initialise a bouncebuffer
*/
void bouncebuffer_init(struct bouncebuffer_t **bouncebuffer, uint32_t prealloc_bytes, bool axi_sram)
{
(*bouncebuffer) = calloc(1, sizeof(struct bouncebuffer_t));
osalDbgAssert(((*bouncebuffer) != NULL), "bouncebuffer init");
(*bouncebuffer)->on_axi_sram = axi_sram;
if (prealloc_bytes) {
(*bouncebuffer)->dma_buf = axi_sram?malloc_axi_sram(prealloc_bytes):malloc_dma(prealloc_bytes);
if ((*bouncebuffer)->dma_buf) {
(*bouncebuffer)->size = prealloc_bytes;
}
}
}
/*
setup for reading from a device into memory, allocating a bouncebuffer if needed
Note that *buf can be NULL, in which case we allocate DMA capable memory, but don't
copy to it in bouncebuffer_finish_read(). This avoids DMA failures in dummyrx in the SPI LLD
*/
bool bouncebuffer_setup_read(struct bouncebuffer_t *bouncebuffer, uint8_t **buf, uint32_t size)
{
if (!bouncebuffer || IS_DMA_SAFE(*buf)) {
// nothing needs to be done
return true;
}
osalDbgAssert((bouncebuffer->busy == false), "bouncebuffer read");
bouncebuffer->orig_buf = *buf;
if (bouncebuffer->size < size) {
if (bouncebuffer->size > 0) {
free(bouncebuffer->dma_buf);
}
bouncebuffer->dma_buf = bouncebuffer->on_axi_sram?malloc_axi_sram(size):malloc_dma(size);
if (!bouncebuffer->dma_buf) {
bouncebuffer->size = 0;
return false;
}
bouncebuffer->size = size;
}
*buf = bouncebuffer->dma_buf;
#if ENABLE_ECHO_SAFE
memset(bouncebuffer->dma_buf, 0xBB, bouncebuffer->size);
#endif
#if defined(STM32H7)
osalDbgAssert((((uint32_t)*buf)&31) == 0, "bouncebuffer read align");
stm32_cacheBufferInvalidate(*buf, (size+31)&~31);
#endif
bouncebuffer->busy = true;
return true;
}
/*
finish a read operation
*/
void bouncebuffer_finish_read(struct bouncebuffer_t *bouncebuffer, const uint8_t *buf, uint32_t size)
{
if (bouncebuffer && buf == bouncebuffer->dma_buf) {
osalDbgAssert((bouncebuffer->busy == true), "bouncebuffer finish_read");
if (bouncebuffer->orig_buf) {
memcpy(bouncebuffer->orig_buf, buf, size);
}
bouncebuffer->busy = false;
}
}
/*
setup for reading from memory to a device, allocating a bouncebuffer if needed
*/
bool bouncebuffer_setup_write(struct bouncebuffer_t *bouncebuffer, const uint8_t **buf, uint32_t size)
{
if (!bouncebuffer || IS_DMA_SAFE(*buf)) {
// nothing needs to be done
return true;
}
osalDbgAssert((bouncebuffer->busy == false), "bouncebuffer write");
if (bouncebuffer->size < size) {
if (bouncebuffer->size > 0) {
free(bouncebuffer->dma_buf);
}
bouncebuffer->dma_buf = bouncebuffer->on_axi_sram?malloc_axi_sram(size):malloc_dma(size);
if (!bouncebuffer->dma_buf) {
bouncebuffer->size = 0;
return false;
}
bouncebuffer->size = size;
}
if (*buf) {
memcpy(bouncebuffer->dma_buf, *buf, size);
}
*buf = bouncebuffer->dma_buf;
#if defined(STM32H7)
osalDbgAssert((((uint32_t)*buf)&31) == 0, "bouncebuffer write align");
stm32_cacheBufferFlush(*buf, (size+31)&~31);
#endif
bouncebuffer->busy = true;
return true;
}
/*
finish a write operation
*/
void bouncebuffer_finish_write(struct bouncebuffer_t *bouncebuffer, const uint8_t *buf)
{
if (bouncebuffer && buf == bouncebuffer->dma_buf) {
osalDbgAssert((bouncebuffer->busy == true), "bouncebuffer finish_wite");
bouncebuffer->busy = false;
}
}
/*
abort an operation
*/
void bouncebuffer_abort(struct bouncebuffer_t *bouncebuffer)
{
if (bouncebuffer) {
bouncebuffer->busy = false;
}
}