| /* | 
 |  * 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; | 
 | } |