You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
3.4 KiB
129 lines
3.4 KiB
/* |
|
* Copyright (c) 2021 Intel Corporation |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
#include <zephyr/sys/util.h> |
|
#include <zephyr/sys/winstream.h> |
|
|
|
/* This code may be used (e.g. for trace/logging) in very early |
|
* environments where the standard library isn't available yet. |
|
* Implement a simple memcpy() as an option. |
|
*/ |
|
#ifndef CONFIG_WINSTREAM_STDLIB_MEMCOPY |
|
# define MEMCPY(dst, src, n) \ |
|
do { for (int i = 0; i < (n); i++) { (dst)[i] = (src)[i]; } } while (0) |
|
#else |
|
# include <string.h> |
|
# define MEMCPY memcpy |
|
#endif |
|
|
|
/* These are just compiler barriers now. Zephyr doesn't currently |
|
* have a framework for hardware memory ordering, and all our targets |
|
* (arm64 excepted) either promise firm ordering (x86) or are in-order |
|
* cores (everything else). But these are marked for future |
|
* enhancement. |
|
*/ |
|
#define READ_BARRIER() __asm__ volatile("" ::: "memory") |
|
#define WRITE_BARRIER() __asm__ volatile("") |
|
|
|
static uint32_t idx_mod(struct sys_winstream *ws, uint32_t idx) |
|
{ |
|
return idx >= ws->len ? idx - ws->len : idx; |
|
} |
|
|
|
/* Computes modular a - b, assuming a and b are in [0:len) */ |
|
static uint32_t idx_sub(struct sys_winstream *ws, uint32_t a, uint32_t b) |
|
{ |
|
return idx_mod(ws, a + (ws->len - b)); |
|
} |
|
|
|
void sys_winstream_write(struct sys_winstream *ws, |
|
const char *data, uint32_t len) |
|
{ |
|
uint32_t len0 = len, suffix; |
|
uint32_t start = ws->start, end = ws->end, seq = ws->seq; |
|
|
|
/* Overflow: if we're truncating then just reset the buffer. |
|
* (Max bytes buffered is actually len-1 because start==end is |
|
* reserved to mean "empty") |
|
*/ |
|
if (len > ws->len - 1) { |
|
start = end; |
|
len = ws->len - 1; |
|
} |
|
|
|
/* Make room in the buffer by advancing start first (note same |
|
* len-1 from above) |
|
*/ |
|
len = MIN(len, ws->len); |
|
if (seq != 0) { |
|
uint32_t avail = (ws->len - 1) - idx_sub(ws, end, start); |
|
|
|
if (len > avail) { |
|
ws->start = idx_mod(ws, start + (len - avail)); |
|
WRITE_BARRIER(); |
|
} |
|
} |
|
|
|
/* Had to truncate? */ |
|
if (len < len0) { |
|
ws->start = end; |
|
data += len0 - len; |
|
} |
|
|
|
suffix = MIN(len, ws->len - end); |
|
MEMCPY(&ws->data[end], data, suffix); |
|
if (len > suffix) { |
|
MEMCPY(&ws->data[0], data + suffix, len - suffix); |
|
} |
|
|
|
ws->end = idx_mod(ws, end + len); |
|
ws->seq += len0; /* seq represents dropped bytes too! */ |
|
WRITE_BARRIER(); |
|
} |
|
|
|
uint32_t sys_winstream_read(struct sys_winstream *ws, |
|
uint32_t *seq, char *buf, uint32_t buflen) |
|
{ |
|
uint32_t seq0 = *seq, start, end, wseq, len, behind, copy, suffix; |
|
|
|
do { |
|
start = ws->start; end = ws->end; wseq = ws->seq; |
|
READ_BARRIER(); |
|
|
|
/* No change in buffer state or empty initial stream are easy */ |
|
if (*seq == wseq || start == end) { |
|
*seq = wseq; |
|
return 0; |
|
} |
|
|
|
/* Underflow: we're trying to read from a spot farther |
|
* back than start. We dropped some bytes, so cheat |
|
* and just drop them all to catch up. |
|
*/ |
|
behind = wseq - *seq; |
|
if (behind > idx_sub(ws, ws->end, ws->start)) { |
|
*seq = wseq; |
|
return 0; |
|
} |
|
|
|
/* Copy data */ |
|
copy = idx_sub(ws, ws->end, behind); |
|
len = MIN(buflen, behind); |
|
suffix = MIN(len, ws->len - copy); |
|
MEMCPY(buf, &ws->data[copy], suffix); |
|
if (len > suffix) { |
|
MEMCPY(buf + suffix, &ws->data[0], len - suffix); |
|
} |
|
*seq = seq0 + len; |
|
|
|
/* Check vs. the state we initially read and repeat if |
|
* needed. This can't loop forever even if the other |
|
* side is stuck spamming writes: we'll run out of |
|
* buffer space and exit via the underflow condition. |
|
*/ |
|
READ_BARRIER(); |
|
} while (start != ws->start || wseq != ws->seq); |
|
|
|
return len; |
|
}
|
|
|