1 /* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <stdlib.h>
7 #include <sys/param.h>
8 
9 #include "cras_types.h"
10 #include "buffer_share.h"
11 
12 static inline struct id_offset *find_unused(const struct buffer_share *mix)
13 {
14 	unsigned int i;
15 
16 	for (i = 0; i < mix->id_sz; i++) {
17 		if (!mix->wr_idx[i].used)
18 			return &mix->wr_idx[i];
19 	}
20 
21 	return NULL;
22 }
23 
24 
25 static inline struct id_offset *find_id(const struct buffer_share *mix,
26 					unsigned int id)
27 {
28 	unsigned int i;
29 
30 	for (i = 0; i < mix->id_sz; i++) {
31 		if (mix->wr_idx[i].used && id == mix->wr_idx[i].id)
32 			return &mix->wr_idx[i];
33 	}
34 
35 	return NULL;
36 }
37 
38 static void alloc_more_ids(struct buffer_share *mix)
39 {
40 	unsigned int new_size = mix->id_sz * 2;
41 	unsigned int i;
42 
43 	mix->wr_idx = realloc(mix->wr_idx, sizeof(mix->wr_idx[0]) * new_size);
44 
45 	for (i = mix->id_sz; i < new_size; i++)
46 		mix->wr_idx[i].used = 0;
47 
48 	mix->id_sz = new_size;
49 }
50 
51 struct buffer_share *buffer_share_create(unsigned int buf_sz)
52 {
53 	struct buffer_share *mix;
54 
55 	mix = calloc(1, sizeof(*mix));
56 	mix->id_sz = INITIAL_ID_SIZE;
57 	mix->wr_idx = calloc(mix->id_sz, sizeof(mix->wr_idx[0]));
58 	mix->buf_sz = buf_sz;
59 
60 	return mix;
61 }
62 
63 void buffer_share_destroy(struct buffer_share *mix)
64 {
65 	if (!mix)
66 		return;
67 	free(mix->wr_idx);
68 	free(mix);
69 }
70 
71 int buffer_share_add_id(struct buffer_share *mix, unsigned int id, void *data)
72 {
73 	struct id_offset *o;
74 
75 	o = find_id(mix, id);
76 	if (o)
77 		return -EEXIST;
78 
79 	o = find_unused(mix);
80 	if (!o)
81 		alloc_more_ids(mix);
82 
83 	o = find_unused(mix);
84 	o->used = 1;
85 	o->id = id;
86 	o->offset = 0;
87 	o->data = data;
88 
89 	return 0;
90 }
91 
92 int buffer_share_rm_id(struct buffer_share *mix, unsigned int id)
93 {
94 	struct id_offset *o;
95 
96 	o = find_id(mix, id);
97 	if (!o)
98 		return -ENOENT;
99 	o->used = 0;
100 	o->data = NULL;
101 
102 	return 0;
103 }
104 
105 int buffer_share_offset_update(struct buffer_share *mix, unsigned int id,
106 			       unsigned int delta)
107 {
108 	unsigned int i;
109 
110 	for (i = 0; i < mix->id_sz; i++) {
111 		if (id != mix->wr_idx[i].id)
112 			continue;
113 
114 		mix->wr_idx[i].offset += delta;
115 		break;
116 	}
117 
118 	return 0;
119 }
120 
121 unsigned int buffer_share_get_new_write_point(struct buffer_share *mix)
122 {
123 	unsigned int min_written = mix->buf_sz + 1;
124 	unsigned int i;
125 
126 	for (i = 0; i < mix->id_sz; i++) {
127 		struct id_offset *o = &mix->wr_idx[i];
128 
129 		if (!o->used)
130 			continue;
131 
132 		min_written = MIN(min_written, o->offset);
133 	}
134 	for (i = 0; i < mix->id_sz; i++) {
135 		struct id_offset *o = &mix->wr_idx[i];
136 		o->offset -= min_written;
137 	}
138 
139 	if (min_written > mix->buf_sz)
140 		return 0;
141 
142 	return min_written;
143 }
144 
145 static struct id_offset *get_id_offset(const struct buffer_share *mix,
146 				       unsigned int id)
147 {
148 	unsigned int i;
149 	struct id_offset *o;
150 
151 	for (i = 0; i < mix->id_sz; i++) {
152 		o = &mix->wr_idx[i];
153 		if (o->used && o->id == id)
154 			return o;
155 	}
156 	return NULL;
157 }
158 
159 unsigned int buffer_share_id_offset(const struct buffer_share *mix,
160 				    unsigned int id)
161 {
162 	struct id_offset *o = get_id_offset(mix, id);
163 	return o ? o->offset : 0;
164 }
165 
166 void *buffer_share_get_data(const struct buffer_share *mix,
167 			    unsigned int id)
168 {
169 	struct id_offset *o = get_id_offset(mix, id);
170 	return o ? o->data : NULL;
171 }
172