blob: 4c312954464ae46472f07d0a50645e1e63301acb [file] [log] [blame]
Travis Geiselbrecht9e604862009-06-28 12:24:07 -07001/*
2 * Copyright (c) 2008 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <stdlib.h>
24#include <debug.h>
25#include <pow2.h>
26#include <string.h>
27#include <lib/cbuf.h>
28#include <kernel/event.h>
29
30#define LOCAL_TRACE 0
31
32#define INC_POINTER(cbuf, ptr, inc) \
33 modpow2(((ptr) + (inc)), (cbuf)->len_pow2)
34
35void cbuf_initialize(cbuf_t *cbuf, size_t len)
36{
37 DEBUG_ASSERT(cbuf);
38 DEBUG_ASSERT(len > 0);
39 DEBUG_ASSERT(ispow2(len));
40
41 cbuf->head = 0;
42 cbuf->tail = 0;
43 cbuf->len_pow2 = log2(len);
44 cbuf->buf = malloc(len);
45 event_init(&cbuf->event, false, 0);
46
47 LTRACEF("len %zd, len_pow2 %u\n", len, cbuf->len_pow2);
48}
49
50static size_t cbuf_space_avail(cbuf_t *cbuf)
51{
52 return (cbuf->head + valpow2(cbuf->len_pow2) - cbuf->tail - 1);
53}
54
55size_t cbuf_write(cbuf_t *cbuf, const void *_buf, size_t len, bool canreschedule)
56{
57 const char *buf = (const char *)_buf;
58
59 LTRACEF("len %zd\n", len);
60
61 DEBUG_ASSERT(cbuf);
62 DEBUG_ASSERT(_buf);
63 DEBUG_ASSERT(len < valpow2(cbuf->len_pow2));
64
65 enter_critical_section();
66
67 size_t write_len;
68 size_t pos = 0;
69
70 while (pos < len && cbuf_space_avail(cbuf) > 0) {
71 if (cbuf->head >= cbuf->tail) {
72 write_len = MIN(valpow2(cbuf->len_pow2) - cbuf->head, len - pos);
73 } else {
74 write_len = MIN(cbuf->tail - cbuf->head - 1, len - pos);
75 }
76
77 // if it's full, abort and return how much we've written
78 if (write_len == 0) {
79 break;
80 }
81
82 memcpy(cbuf->buf + cbuf->head, buf + pos, write_len);
83
84 cbuf->head = INC_POINTER(cbuf, cbuf->head, write_len);
85 pos += write_len;
86 }
87
88 if (cbuf->head != cbuf->tail)
89 event_signal(&cbuf->event, canreschedule);
90
91 exit_critical_section();
92
93 return pos;
94}
95
96size_t cbuf_read(cbuf_t *cbuf, void *_buf, size_t buflen, bool block)
97{
98 size_t ret;
99 char *buf = (char *)_buf;
100
101 DEBUG_ASSERT(cbuf);
102 DEBUG_ASSERT(_buf);
103
104 enter_critical_section();
105
106 if (block)
107 event_wait(&cbuf->event);
108
109 // see if there's data available
110 if (cbuf->tail != cbuf->head) {
111 size_t pos = 0;
112
113 // loop until we've read everything we need
114 // at most this will make two passes to deal with wraparound
115 while (pos < buflen && cbuf->tail != cbuf->head) {
116 size_t read_len;
117 if (cbuf->head > cbuf->tail) {
118 // simple case where there is no wraparound
119 read_len = MIN(cbuf->head - cbuf->tail, buflen - pos);
120 } else {
121 // read to the end of buffer in this pass
122 read_len = MIN(valpow2(cbuf->len_pow2) - cbuf->tail, buflen - pos);
123 }
124
125 memcpy(buf + pos, cbuf->buf + cbuf->tail, read_len);
126
127 cbuf->tail = INC_POINTER(cbuf, cbuf->tail, read_len);
128 pos += read_len;
129 }
130
131 if (cbuf->tail == cbuf->head) {
132 // we've emptied the buffer, unsignal the event
133 event_unsignal(&cbuf->event);
134 }
135
136 ret = pos;
137 } else {
138 DEBUG_ASSERT(!block);
139 ret = 0;
140 }
141
142 exit_critical_section();
143
144 return ret;
145}
146