1// Copyright 2016 The Fuchsia Authors 2// Copyright (c) 2008-2015 Travis Geiselbrecht 3// 4// Use of this source code is governed by a MIT-style 5// license that can be found in the LICENSE file or at 6// https://opensource.org/licenses/MIT 7 8#include <lib/cbuf.h> 9 10#include <assert.h> 11#include <debug.h> 12#include <fbl/algorithm.h> 13#include <kernel/auto_lock.h> 14#include <kernel/event.h> 15#include <kernel/spinlock.h> 16#include <pow2.h> 17#include <stdlib.h> 18#include <string.h> 19#include <trace.h> 20 21#define LOCAL_TRACE 0 22 23static inline uint inc_pointer(const cbuf_t* cbuf, uint ptr, uint inc) { 24 return modpow2(ptr + inc, cbuf->len_pow2); 25} 26 27void cbuf_initialize(cbuf_t* cbuf, size_t len) { 28 cbuf_initialize_etc(cbuf, len, malloc(len)); 29} 30 31void cbuf_initialize_etc(cbuf_t* cbuf, size_t len, void* buf) { 32 DEBUG_ASSERT(cbuf); 33 DEBUG_ASSERT(len > 0); 34 DEBUG_ASSERT(fbl::is_pow2(len)); 35 36 cbuf->head = 0; 37 cbuf->tail = 0; 38 cbuf->len_pow2 = log2_ulong_floor(len); 39 cbuf->buf = static_cast<char*>(buf); 40 event_init(&cbuf->event, false, 0); 41 spin_lock_init(&cbuf->lock); 42 43 LTRACEF("len %zu, len_pow2 %u\n", len, cbuf->len_pow2); 44} 45 46size_t cbuf_space_avail(const cbuf_t* cbuf) { 47 uint consumed = modpow2(cbuf->head - cbuf->tail, cbuf->len_pow2); 48 return valpow2(cbuf->len_pow2) - consumed - 1; 49} 50 51size_t cbuf_write_char(cbuf_t* cbuf, char c) { 52 DEBUG_ASSERT(cbuf); 53 54 size_t ret = 0; 55 { 56 AutoSpinLock guard(&cbuf->lock); 57 58 if (cbuf_space_avail(cbuf) > 0) { 59 cbuf->buf[cbuf->head] = c; 60 61 cbuf->head = inc_pointer(cbuf, cbuf->head, 1); 62 ret = 1; 63 } 64 } 65 66 if (ret > 0) { 67 event_signal(&cbuf->event, true); 68 } 69 70 return ret; 71} 72 73size_t cbuf_read_char(cbuf_t* cbuf, char* c, bool block) { 74 DEBUG_ASSERT(cbuf); 75 DEBUG_ASSERT(c); 76 77retry: 78 if (block) { 79 event_wait(&cbuf->event); 80 } 81 82 size_t ret = 0; 83 { 84 AutoSpinLock guard(&cbuf->lock); 85 86 // see if there's data available 87 if (cbuf->tail != cbuf->head) { 88 89 *c = cbuf->buf[cbuf->tail]; 90 cbuf->tail = inc_pointer(cbuf, cbuf->tail, 1); 91 92 if (cbuf->tail == cbuf->head) { 93 // we've emptied the buffer, unsignal the event 94 event_unsignal(&cbuf->event); 95 } 96 97 ret = 1; 98 } 99 } 100 101 if (block && ret == 0) { 102 goto retry; 103 } 104 105 return ret; 106} 107