312 lines
8.1 KiB
C
312 lines
8.1 KiB
C
/**
|
|
******************************************************************************
|
|
* @file ringbuf.c
|
|
* @brief driver for a ring buffer used for UART RX
|
|
******************************************************************************
|
|
* @author: Thomas Kuschel KW4NZ
|
|
* created 2022-07-12
|
|
*
|
|
* A description can be found in the header file ringbuf.h
|
|
******************************************************************************/
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h> /* malloc */
|
|
#include <stdio.h> /* printf */
|
|
#include <string.h> /* memcpy */
|
|
#include "ringbuf.h"
|
|
|
|
/* macros */
|
|
#define MEM_USED(b) ((b->size + b->head - b->tail) % b->size)
|
|
#define MEM_FREE(b) ((b->size + b->tail - b->head - 1) % b->size + 1)
|
|
|
|
#if RING_STATISTICS_ENABLED
|
|
typedef struct stat {
|
|
uint32_t overflows;
|
|
uint32_t reads;
|
|
uint32_t writes;
|
|
} stat_t;
|
|
#endif
|
|
|
|
typedef struct ringbuf {
|
|
#if RING_STATISTICS_ENABLED
|
|
stat_t statistics;
|
|
#endif
|
|
ringbuf_rcv_cb_t rcv_callback;
|
|
void *rcv_cb_data;
|
|
uint8_t *buf;
|
|
uint16_t size;
|
|
uint16_t head;
|
|
uint16_t tail;
|
|
uint16_t delimiterfound;
|
|
uint16_t max_read_len;
|
|
unsigned full :1; // not implemented yet
|
|
unsigned halffull:1; // not implemented yet
|
|
unsigned overflow:1;
|
|
unsigned allowoverwrite:1;
|
|
} ringbuf_t;
|
|
|
|
struct ringbuf * ringbuf_create(size_t size, ringbuf_param_t param) {
|
|
|
|
struct ringbuf * rb;
|
|
if (size == 0 || size > USHRT_MAX)
|
|
return NULL;
|
|
|
|
// rb = malloc(sizeof(ringbuf_t) + size * sizeof(uint8_t));
|
|
rb = calloc(1, sizeof(ringbuf_t) + size * sizeof(uint8_t));
|
|
if (rb == NULL) {
|
|
puts("Memory not allocated.");
|
|
exit(0);
|
|
} else {
|
|
//puts("Memory successfully allocated.");
|
|
// the data area is connected to the structure
|
|
rb->buf = (uint8_t *)rb + sizeof(ringbuf_t);
|
|
rb->size = (uint16_t)size;
|
|
// rb->head = rb->tail = rb->halffull = rb->overflow 0;
|
|
if (param & RINGBUF_ALLOWOVERWRITE)
|
|
rb->allowoverwrite = 1;
|
|
rb->max_read_len = RINGBUF_MAX_READ_LEN;
|
|
}
|
|
return rb;
|
|
}
|
|
|
|
void ringbuf_destroy(struct ringbuf *ring) {
|
|
if (ring != 0)
|
|
free(ring);
|
|
}
|
|
|
|
int ringbuf_dump(struct ringbuf *ring) {
|
|
|
|
if (ring == NULL)
|
|
return -EINVAL;
|
|
printf("Buffer: 0x%08x\n", (unsigned int) ring);
|
|
printf("Start: 0x%08x\n", (unsigned int) ring->buf);
|
|
printf("Size: total: %d used: %d free: %d\n", ring->size, MEM_USED(ring), MEM_FREE(ring));
|
|
printf("Head: %d\n", ring->head);
|
|
printf("Tail: %d\n", ring->tail);
|
|
printf("Empty: %s\n", (ring->head == ring->tail) ? "yes" : "no");
|
|
printf("Max read length: %d\n", ring->max_read_len);
|
|
for (size_t i = 0; i < ring->size; i += 16 ) {
|
|
for (size_t j = 0; j < 16 && (j + i) < ring->size; j++) {
|
|
//printf("%02x%s", *(ring->buf +i), ((i%4)==3)?(((i%16)==15)?"\n":" "):"");
|
|
printf("%02x%s", *(ring->buf + i + j), ((j%4)==3)?" ":"");
|
|
}
|
|
putchar(' ');
|
|
for (size_t j = 0; j < 16 && (j + i) < ring->size; j++) {
|
|
uint8_t b = *(ring->buf + i + j);
|
|
printf("%c", ((b >= ' ') && (b < 127))? b: '.');
|
|
}
|
|
puts("");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ringbuf_push(struct ringbuf *ring, const uint8_t *data, size_t size) {
|
|
|
|
size_t delimiterpos;
|
|
size_t delimiterfound = 0;
|
|
uint16_t head;
|
|
uint8_t *ptr;
|
|
|
|
if (ring == NULL || size == 0 || data == NULL || size > USHRT_MAX)
|
|
return -EINVAL;
|
|
|
|
if (size >= ring->size)
|
|
return -ENOMEM;
|
|
|
|
if (size >= (size_t)MEM_FREE(ring)) { // no free space available, but overwrite ?
|
|
#if RING_STATISTICS_ENABLED
|
|
ring->statistics.overflows++;
|
|
#endif
|
|
if (ring->allowoverwrite)
|
|
ring->overflow = 1;
|
|
else
|
|
return -ENOMEM;
|
|
}
|
|
|
|
head = ring->head;
|
|
if (head + size > ring->size) {
|
|
uint16_t remaining = ring->size - head;
|
|
memcpy(ring->buf + head, data, remaining);
|
|
ring->head = (uint16_t)(size - remaining);
|
|
memcpy(ring->buf, data + remaining, ring->head);
|
|
} else {
|
|
memcpy(ring->buf + head, data, size);
|
|
ring->head += (uint16_t)size;
|
|
}
|
|
for (delimiterpos = 0; delimiterpos < size; delimiterpos++) {
|
|
if (data[delimiterpos] == RINGBUF_DELIMITER || data[delimiterpos] == 0 ) {
|
|
delimiterfound++;
|
|
ptr = ring->buf + ((head + delimiterpos) % ring->size);
|
|
*ptr = 0;
|
|
}
|
|
}
|
|
ring->head %= ring->size;
|
|
|
|
#if RING_STATISTICS_ENABLED
|
|
ring->statistics.writes +=size;
|
|
#endif
|
|
ring->delimiterfound = (uint16_t)delimiterfound;
|
|
//call registered callback function
|
|
if (ring->delimiterfound && ring->rcv_callback != NULL)
|
|
ring->rcv_callback(ring->delimiterfound, ring->rcv_cb_data);
|
|
return (int)size;
|
|
}
|
|
|
|
int ringbuf_clear(struct ringbuf *ring) {
|
|
if (ring == NULL)
|
|
return -EINVAL;
|
|
ring->tail = ring->head;
|
|
ring->full = ring->halffull = ring->overflow = 0;
|
|
return 0;
|
|
}
|
|
|
|
int ringbuf_is_empty(struct ringbuf *ring) {
|
|
if (ring == NULL)
|
|
return -EINVAL;
|
|
return (ring->tail == ring->head);
|
|
}
|
|
|
|
int ringbuf_pull(struct ringbuf *ring, uint8_t *data, size_t maxsize) {
|
|
|
|
size_t datasize;
|
|
|
|
if (ring == NULL || maxsize == 0 || data == NULL || maxsize > USHRT_MAX)
|
|
return -EINVAL;
|
|
datasize = min(maxsize, (size_t)MEM_USED(ring));
|
|
if (datasize > ring->size)
|
|
return -ENOMEM;
|
|
|
|
if ((ring->head > ring->tail) /*|| ring->full*/) {
|
|
memcpy(data, ring->buf + ring->tail, datasize);
|
|
ring->tail += (uint16_t)datasize;
|
|
} else {
|
|
if (ring->head < ring->tail) {
|
|
size_t remaining = ring->size -ring->tail;
|
|
if (datasize < remaining) {
|
|
memcpy(data, ring->buf + ring->tail, datasize);
|
|
ring->tail += (uint16_t)datasize;
|
|
} else {
|
|
memcpy(data, ring->buf + ring->tail, remaining);
|
|
ring->tail = (uint16_t)(datasize - remaining);
|
|
memcpy(data + remaining, ring->buf, ring->tail);
|
|
}
|
|
}
|
|
}
|
|
#if RING_STATISTICS_ENABLED
|
|
ring->statistics.reads += datasize;
|
|
#endif
|
|
ring->overflow = 0;
|
|
return (int)datasize;
|
|
}
|
|
|
|
int ringbuf_read(struct ringbuf *ring, char *str) {
|
|
|
|
int len = 0;
|
|
uint16_t tail = 0;
|
|
|
|
if (ring == NULL || str == NULL)
|
|
return -EINVAL;
|
|
|
|
*str = '\0';
|
|
if (ring->head == ring->tail){
|
|
return 0;
|
|
}
|
|
tail = ring->tail;
|
|
if (ring->head > ring->tail) {
|
|
if (ring->max_read_len < 2)
|
|
return -ENOMEM;
|
|
strncpy(str, (char *)(ring->buf + ring->tail), ring->max_read_len);
|
|
str[ring->max_read_len] = '\0';
|
|
for (int i = ring->tail; i < ring->head; i++) {
|
|
if (*(ring->buf + i) == 0) {
|
|
ring->tail = (uint16_t)i + 1;
|
|
break;
|
|
}
|
|
}
|
|
if (ring->tail == tail) {
|
|
// no \0 found
|
|
return 0;
|
|
}
|
|
} else {
|
|
int continu = 1;
|
|
int tail = 0;
|
|
strncpy(str, (char *)(ring->buf + ring->tail),(size_t)min(ring->size - ring->tail, ring->max_read_len));
|
|
str[ring->max_read_len] = '\0';
|
|
len = (int)strlen(str);
|
|
for (int i = ring->tail; i < ring->size; i++) {
|
|
if (*(ring->buf + i) == 0) {
|
|
tail = i + 1;
|
|
continu = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (continu) {
|
|
strncpy(str + ring->size - ring->tail, (char *)ring->buf, (size_t)(ring->max_read_len - ring->size + ring->tail));
|
|
str[ring->max_read_len] = '\0';
|
|
continu = 1;
|
|
for (int i = 0; i < ring->head; i++) {
|
|
if (*(ring->buf + i) == 0) {
|
|
tail = i + 1;
|
|
continu = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (continu)
|
|
return -EINVAL;
|
|
}
|
|
ring->tail = (uint16_t)tail;
|
|
}
|
|
len = (int)strlen(str);
|
|
ring->tail = ring->tail % ring->size;
|
|
#if RING_STATISTICS_ENABLED
|
|
ring->statistics.reads += (uint32_t)len + 1;
|
|
#endif
|
|
return len;
|
|
}
|
|
|
|
int ringbuf_write(struct ringbuf *ring, char *str) {
|
|
|
|
size_t len = 0;
|
|
len = (size_t)strlen(str);
|
|
if (len > 0)
|
|
return ringbuf_push(ring, (uint8_t *)str, len + 1);
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
|
|
#if RING_STATISTICS_ENABLED
|
|
int ringbuf_statistics(struct ringbuf *ring) {
|
|
if (ring == NULL)
|
|
return -EINVAL;
|
|
puts("Ring Buffer Statistics:");
|
|
printf(" Bytes written: %ld\n", ring->statistics.writes);
|
|
printf(" Bytes read: %ld\n", ring->statistics.reads);
|
|
printf("# of overflows: %ld\n", ring->statistics.overflows);
|
|
return 0;
|
|
}
|
|
int ringbuf_stat_writes(struct ringbuf *ring) {
|
|
if (ring == NULL)
|
|
return -EINVAL;
|
|
return (int)ring->statistics.writes;
|
|
}
|
|
int ringbuf_stat_reads(struct ringbuf *ring) {
|
|
if (ring == NULL)
|
|
return -EINVAL;
|
|
return (int)ring->statistics.reads;
|
|
}
|
|
int ringbuf_stat_overflow(struct ringbuf *ring) {
|
|
if (ring == NULL)
|
|
return -EINVAL;
|
|
return (int)ring->statistics.overflows;
|
|
}
|
|
#endif
|
|
|
|
int ringbuf_callback_register(struct ringbuf *ring, ringbuf_rcv_cb_t cb_func, void *cb_data) {
|
|
if (ring == NULL)
|
|
return -EINVAL;
|
|
ring->rcv_callback = cb_func;
|
|
ring->rcv_cb_data = cb_data;
|
|
return 0;
|
|
}
|