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