blob: 3aac6384eb00d99ba8e53a88aa37f19b731002f2 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2015 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/cbuf.h>
#include <assert.h>
#include <debug.h>
#include <fbl/algorithm.h>
#include <kernel/auto_lock.h>
#include <kernel/event.h>
#include <kernel/spinlock.h>
#include <pow2.h>
#include <stdlib.h>
#include <string.h>
#include <trace.h>
#define LOCAL_TRACE 0
static inline uint inc_pointer(const cbuf_t* cbuf, uint ptr, uint inc) {
return modpow2(ptr + inc, cbuf->len_pow2);
}
void cbuf_initialize(cbuf_t* cbuf, size_t len) {
cbuf_initialize_etc(cbuf, len, malloc(len));
}
void cbuf_initialize_etc(cbuf_t* cbuf, size_t len, void* buf) {
DEBUG_ASSERT(cbuf);
DEBUG_ASSERT(len > 0);
DEBUG_ASSERT(fbl::is_pow2(len));
cbuf->head = 0;
cbuf->tail = 0;
cbuf->len_pow2 = log2_ulong_floor(len);
cbuf->buf = static_cast<char*>(buf);
event_init(&cbuf->event, false, 0);
spin_lock_init(&cbuf->lock);
LTRACEF("len %zu, len_pow2 %u\n", len, cbuf->len_pow2);
}
size_t cbuf_space_avail(const cbuf_t* cbuf) {
uint consumed = modpow2(cbuf->head - cbuf->tail, cbuf->len_pow2);
return valpow2(cbuf->len_pow2) - consumed - 1;
}
size_t cbuf_write_char(cbuf_t* cbuf, char c) {
DEBUG_ASSERT(cbuf);
size_t ret = 0;
{
AutoSpinLock guard(&cbuf->lock);
if (cbuf_space_avail(cbuf) > 0) {
cbuf->buf[cbuf->head] = c;
cbuf->head = inc_pointer(cbuf, cbuf->head, 1);
ret = 1;
}
}
if (ret > 0) {
event_signal(&cbuf->event, true);
}
return ret;
}
size_t cbuf_read_char(cbuf_t* cbuf, char* c, bool block) {
DEBUG_ASSERT(cbuf);
DEBUG_ASSERT(c);
retry:
if (block) {
event_wait(&cbuf->event);
}
size_t ret = 0;
{
AutoSpinLock guard(&cbuf->lock);
// see if there's data available
if (cbuf->tail != cbuf->head) {
*c = cbuf->buf[cbuf->tail];
cbuf->tail = inc_pointer(cbuf, cbuf->tail, 1);
if (cbuf->tail == cbuf->head) {
// we've emptied the buffer, unsignal the event
event_unsignal(&cbuf->event);
}
ret = 1;
}
}
if (block && ret == 0) {
goto retry;
}
return ret;
}