blob: 4e5e458481d22b4fd897c5512a1bfd7851c8362c [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 <assert.h>
#include <debug.h>
#include <lib/cbuf.h>
#include <stdlib.h>
#include <string.h>
#include <trace.h>
#include <fbl/algorithm.h>
#include <kernel/auto_lock.h>
#include <kernel/event.h>
#include <kernel/spinlock.h>
#define LOCAL_TRACE 0
// This should only be called once to initialize the Cbuf, and so thread safety analysis is
// disabled.
void Cbuf::Initialize(size_t len, void* buf) TA_NO_THREAD_SAFETY_ANALYSIS {
DEBUG_ASSERT(len > 0);
DEBUG_ASSERT(fbl::is_pow2(len));
len_pow2_ = log2_ulong_floor(len);
buf_ = static_cast<char*>(buf);
LTRACEF("len %zu, len_pow2 %u\n", len, len_pow2_);
}
// TODO(fxb/48878) We want to revisit the Cbuf API. It's intended to be used from interrupt context,
// at which time clients can rely on being the only accessor. For now, we disable thread safety
// analysis on this function.
size_t Cbuf::SpaceAvail() const TA_NO_THREAD_SAFETY_ANALYSIS {
uint32_t consumed = modpow2(head_ - tail_, len_pow2_);
return valpow2(len_pow2_) - consumed - 1;
}
size_t Cbuf::WriteChar(char c) {
size_t ret = 0;
{
AutoSpinLock guard(&lock_);
if (SpaceAvail() > 0) {
buf_[head_] = c;
IncPointer(&head_, 1);
ret = 1;
}
}
if (ret > 0) {
event_.Signal();
}
return ret;
}
size_t Cbuf::ReadChar(char* c, bool block) {
DEBUG_ASSERT(c);
retry:
if (block) {
event_.Wait(Deadline::infinite());
}
size_t ret = 0;
{
AutoSpinLock guard(&lock_);
// see if there's data available
if (tail_ != head_) {
*c = buf_[tail_];
IncPointer(&tail_, 1);
if (tail_ == head_) {
// We've emptied the buffer, so unsignal the event.
event_.Unsignal();
}
ret = 1;
}
}
if (block && ret == 0) {
goto retry;
}
return ret;
}