/* Reflow Oven Controller
 *
 * Copyright (C) 2020  Mario Hüttel <mario.huettel@gmx.net>
 *
 * This file is part of the Reflow Oven Controller Project.
 *
 * The reflow oven controller is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * GDSII-Converter 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 the reflow oven controller project.
 * If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file dma-ring-buffer.h
 * @brief Header for DMA Ring buffer implemenation
 */

/**
 * @defgroup dma-ring-buffer DMA Ring Buffers from and to Peripherals
 */

#ifndef __DMA_RING_BUFFER_H__
#define __DMA_RING_BUFFER_H__

#include <stdbool.h>
#include <stm32/stm32f4xx.h>
#include <stddef.h>

/**
 * @addtogroup dma-ring-buffer
 * @{
 */

/**
 * @brief DMA ring buffer for data transfer from peripheral to memory
 */
struct dma_ring_buffer_to_mem {
	volatile void *data_ptr;			/**< @brief Ring buffer */
	size_t buffer_count;		/**< @brief Size of buffer in multiples of elements */
	DMA_Stream_TypeDef *dma;	/**< @brief DMA Stream used to transfer data */
	size_t get_idx;			/**< @brief Get index for SW. Indicates the next element to be read */
	uint8_t base_dma_id;		/**< @brief Id of the DMA controller, the stream belongs to. Either 1 or 2. */
	size_t element_size;		/**< @brief Size of a buffer element (1, 2, or 4 bytes) */
};

/**
 * @brief DMA ring buffer for data transfer from memory to peripheral
 */
struct dma_ring_buffer_to_periph {
	volatile void *src_buffer;			/**< @brief Ring buffer */
	size_t buffer_count;			/**< @brief Size of buffer in multiples of elements */
	DMA_Stream_TypeDef *dma;		/**< @brief DMA Stream used to transfer data */
	volatile size_t dma_get_idx_current;	/**< @brief Current get index of the (not yet) running DMA Stream. */
	volatile size_t dma_get_idx_future;	/**< @brief Get index in the buffer, after the current DMA transfer has finished */
	volatile size_t sw_put_idx;		/**< @brief Put index for software */
	uint8_t dma_base_id;			/**< @brief Id of the DMA controller, the stream belongs to. Either 1 or 2. */
	size_t element_size;			/**< @brief Size of a buffer element (1, 2, or 4 bytes) */
};

/**
 * @brief Initialize a ring buffer used for transferring data from a peripheral to memory.
 * @param[in,out] dma_buffer structure representing the newly initialized ring buffer
 * @param base_dma_id Id of the DMA controller, the stream belongs to. Either 1 or 2
 * @param[in] dma_stream DMA Stream to use
 * @param buffer_element_count Size of buffer in elements
 * @param element_size Size of an element. Either 1, 2, or 4 bytes
 * @param[in] data_buffer Buffer to operate on
 * @param[in] src_reg Source register to read data from
 * @param dma_trigger_channel Trigger channel for DMA stream. Consult reference manual for a valid number.
 * @note The ring buffers do not have an overrun detection. You have to make sure to empty the buffer in time.
 * @return Status (0 is ok)
 */
int dma_ring_buffer_periph_to_mem_initialize(struct dma_ring_buffer_to_mem *dma_buffer, uint8_t base_dma_id, DMA_Stream_TypeDef *dma_stream, size_t buffer_element_count, size_t element_size, volatile void *data_buffer, void *src_reg, uint8_t dma_trigger_channel);

/**
 * @brief Get data from a peripheral to memory ring buffer
 * @param[in] buff Ring buffer structure
 * @param[out] data_buff Pointer to set to new data. This must not be modified!
 * @param[out] len Length in elements
 * @return 0 if successful, but no data), -1 if error, 1 if data, and 2 if data with wrap around. Call function again in this case to retrieve rest after wrap around.
 */
int dma_ring_buffer_periph_to_mem_get_data(struct dma_ring_buffer_to_mem *buff, const volatile void **data_buff, size_t *len);

/**
 * @brief Stop the ring buffer operation.
 * @note The ring buffer loses all its configuration after this call.
 * @param buff Ring buffer to stop
 */
void dma_ring_buffer_periph_to_mem_stop(struct dma_ring_buffer_to_mem *buff);

/**
 * @brief Get fill level of peripheral to memory DMA ring buffer
 * @param buff Buffer
 * @param fill_level fill level to write data to
 * @return 0 if success
 */
int dma_ring_buffer_periph_to_mem_fill_level(struct dma_ring_buffer_to_mem *buff, size_t *fill_level);

/**
 * @brief Initialize ring buffer to streaming data from meory to a peripheral
 * @param[in,out] dma_buffer DMA ring buffer structure
 * @param base_dma_id Id of the DMA controller, the stream belongs to. Either 1 or 2
 * @param[in] dma_stream DMA stream to use
 * @param buffer_element_count size of ring buffer in elements
 * @param element_size Size of an element. Either 1, 2, or 4 bytes
 * @param[in] data_buffer Ring buffer
 * @param dma_trigger_channel Trigger channel to start DMA. Consult reference manual for values
 * @param[in] dest_reg Destination register to stream data to
 * @return 0 if successful
 */
int dma_ring_buffer_mem_to_periph_initialize(struct dma_ring_buffer_to_periph *dma_buffer, uint8_t base_dma_id, DMA_Stream_TypeDef *dma_stream, size_t buffer_element_count, size_t element_size, volatile void *data_buffer, uint8_t dma_trigger_channel, void *dest_reg);

/**
 * @brief Insert data into the ring buffer
 *
 * This function will try to insert data into the ring buffer. If the buffer is full, it will wait for the buffer to become empty,
 * until the data fits into the buffer. If the data is larger than the full buffer, it is split. The buffer is fully written. The rest is written after the buffer has emptied out,
 * until the rest fits in
 *
 * @param buff Ring buffer element
 * @param data_to_insert Data to put in buffer
 * @param count Element count of data to insert
 * @return 0 if successful.
 */
int dma_ring_buffer_mem_to_periph_insert_data(struct dma_ring_buffer_to_periph *buff, const void *data_to_insert, size_t count);

/**
 * @brief Call this function on a transfer complete interrupt of the DMA.
 * @note It is mandatory to call this function in order to provide a working ring buffer.
 * @param buff Ring buffer struct
 */
void dma_ring_buffer_mem_to_periph_int_callback(struct dma_ring_buffer_to_periph *buff);

/**
 * @brief Stop the ring buffer operation.
 * @note The ring buffer loses all its configuration after this call.
 * @param buff Ring buffer to stop
 */
void dma_ring_buffer_mem_to_periph_stop(struct dma_ring_buffer_to_periph *buff);

/**
 * @brief Get fill level of mem to periph DMA ring buffer
 * @param buff Buffer
 * @param fill_level fill level to write data to
 * @return 0 if success
 */
int dma_ring_buffer_mem_to_periph_fill_level(struct dma_ring_buffer_to_periph *buff, size_t *fill_level);

/** @} */

#endif /* __DMA_RING_BUFFER_H__ */



