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
inc_pointer(const cbuf_t * cbuf,uint ptr,uint inc)23 static inline uint inc_pointer(const cbuf_t* cbuf, uint ptr, uint inc) {
24 return modpow2(ptr + inc, cbuf->len_pow2);
25 }
26
cbuf_initialize(cbuf_t * cbuf,size_t len)27 void cbuf_initialize(cbuf_t* cbuf, size_t len) {
28 cbuf_initialize_etc(cbuf, len, malloc(len));
29 }
30
cbuf_initialize_etc(cbuf_t * cbuf,size_t len,void * buf)31 void 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
cbuf_space_avail(const cbuf_t * cbuf)46 size_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
cbuf_write_char(cbuf_t * cbuf,char c)51 size_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
cbuf_read_char(cbuf_t * cbuf,char * c,bool block)73 size_t cbuf_read_char(cbuf_t* cbuf, char* c, bool block) {
74 DEBUG_ASSERT(cbuf);
75 DEBUG_ASSERT(c);
76
77 retry:
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