/** ****************************************************************************** * @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 #include #include #include /* malloc */ #include /* printf */ #include /* 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; }