xref: /freebsd/sys/dev/sound/pcm/feeder.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
1 /*
2  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <dev/sound/pcm/sound.h>
30 
31 #define MAXFEEDERS 	256
32 #undef FEEDER_DEBUG
33 
34 struct feedertab_entry {
35 	SLIST_ENTRY(feedertab_entry) link;
36 	pcm_feeder *feeder;
37 	struct pcm_feederdesc *desc;
38 
39 	int idx;
40 };
41 static SLIST_HEAD(, feedertab_entry) feedertab;
42 
43 /*****************************************************************************/
44 
45 void
46 feeder_register(void *p)
47 {
48 	pcm_feeder *f = p;
49 	struct feedertab_entry *fte;
50 	static int feedercnt = 0;
51 	int i;
52 
53 	if (feedercnt == 0) {
54 		if (f->desc)
55 			panic("FIRST FEEDER NOT ROOT: %s\n", f->name);
56 		SLIST_INIT(&feedertab);
57 		fte = malloc(sizeof(*fte), M_DEVBUF, M_NOWAIT);
58 		fte->feeder = f;
59 		fte->desc = NULL;
60 		fte->idx = feedercnt;
61 		SLIST_INSERT_HEAD(&feedertab, fte, link);
62 		feedercnt++;
63 		return;
64 	}
65 	/* printf("installing feeder: %s\n", f->name); */
66 
67 	i = 0;
68 	while ((feedercnt < MAXFEEDERS) && (f->desc[i].type > 0)) {
69 		fte = malloc(sizeof(*fte), M_DEVBUF, M_NOWAIT);
70 		fte->feeder = f;
71 		fte->desc = &f->desc[i];
72 		fte->idx = feedercnt;
73 		fte->desc->idx = feedercnt;
74 		SLIST_INSERT_HEAD(&feedertab, fte, link);
75 		i++;
76 	}
77 	feedercnt++;
78 	if (feedercnt >= MAXFEEDERS)
79 		printf("MAXFEEDERS exceeded\n");
80 }
81 
82 static int
83 cmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m)
84 {
85 	return ((n->type == m->type) &&
86 		((n->in == 0) || (n->in == m->in)) &&
87 		((n->out == 0) || (n->out == m->out)) &&
88 		(n->flags == m->flags));
89 }
90 
91 pcm_feeder *
92 feeder_get(struct pcm_feederdesc *desc)
93 {
94 	struct feedertab_entry *fte;
95 
96 	SLIST_FOREACH(fte, &feedertab, link) {
97 		if ((fte->desc != NULL) && cmpdesc(desc, fte->desc))
98 			return fte->feeder;
99 	}
100 	return NULL;
101 }
102 
103 pcm_feeder *
104 feeder_getroot()
105 {
106 	struct feedertab_entry *fte;
107 
108 	SLIST_FOREACH(fte, &feedertab, link) {
109 		if (fte->desc == NULL)
110 			return fte->feeder;
111 	}
112 	return NULL;
113 }
114 
115 int
116 feeder_set(pcm_feeder *feeder, int what, int value)
117 {
118 	if (feeder->set)
119 		return feeder->set(feeder, what, value);
120 	else
121 		return -1;
122 }
123 
124 int
125 chn_addfeeder(pcm_channel *c, pcm_feeder *f)
126 {
127 	pcm_feeder *nf;
128 	struct pcm_feederdesc *nfdesc;
129 
130 	nf = malloc(sizeof(*nf), M_DEVBUF, M_NOWAIT);
131 	nfdesc = malloc(sizeof(*nfdesc), M_DEVBUF, M_NOWAIT);
132         *nfdesc = *(f->desc);
133 	*nf = *f;
134 	nf->desc = nfdesc;
135 	nf->source = c->feeder;
136 	if (nf->init)
137 		nf->init(nf);
138 	if (nf->align > 0)
139 		c->align += nf->align;
140 	else if (nf->align < 0 && c->align < -nf->align)
141 		c->align = -nf->align;
142 
143 	c->feeder = nf;
144 
145 	return 0;
146 }
147 
148 int
149 chn_removefeeder(pcm_channel *c)
150 {
151 	pcm_feeder *f;
152 
153 	if (c->feeder->source == NULL)
154 		return -1;
155 	f = c->feeder->source;
156 	if (c->feeder->free)
157 		c->feeder->free(c->feeder);
158 	free(c->feeder->desc, M_DEVBUF);
159 	free(c->feeder, M_DEVBUF);
160 	c->feeder = f;
161 	return 0;
162 }
163 
164 pcm_feeder *
165 chn_findfeeder(pcm_channel *c, u_int32_t type)
166 {
167 	pcm_feeder *f;
168 
169 	f = c->feeder;
170 	while (f != NULL) {
171 		if (f->desc->type == type)
172 			return f;
173 		f = f->source;
174 	}
175 	return NULL;
176 }
177 
178 static int
179 chainok(pcm_feeder *test, pcm_feeder *stop)
180 {
181 	u_int32_t visited[MAXFEEDERS / 32];
182 	u_int32_t idx, mask;
183 
184 	bzero(visited, sizeof(visited));
185 	while (test && (test != stop)) {
186 		idx = test->desc->idx;
187 		if (idx < 0)
188 			panic("bad idx %d", idx);
189 		if (idx >= MAXFEEDERS)
190 			panic("bad idx %d", idx);
191 		mask = 1 << (idx & 31);
192 		idx >>= 5;
193 		if (visited[idx] & mask)
194 			return 0;
195 		visited[idx] |= mask;
196 		test = test->source;
197 	}
198 	return 1;
199 }
200 
201 static pcm_feeder *
202 feeder_fmtchain(u_int32_t *to, pcm_feeder *source, pcm_feeder *stop, int maxdepth)
203 {
204 	struct feedertab_entry *fte;
205 	pcm_feeder *try, *ret;
206 	struct pcm_feederdesc *trydesc;
207 
208 	/* printf("trying %s...\n", source->name); */
209 	if (fmtvalid(source->desc->out, to)) {
210 		/* printf("got it\n"); */
211 		return source;
212 	}
213 
214 	if (maxdepth < 0)
215 		return NULL;
216 
217 	try = malloc(sizeof(*try), M_DEVBUF, M_NOWAIT);
218 	trydesc = malloc(sizeof(*trydesc), M_DEVBUF, M_NOWAIT);
219 	trydesc->type = FEEDER_FMT;
220 	trydesc->in = source->desc->out;
221 	trydesc->out = 0;
222 	trydesc->flags = 0;
223 	trydesc->idx = -1;
224 
225 	SLIST_FOREACH(fte, &feedertab, link) {
226 		if ((fte->desc) && (fte->desc->in == source->desc->out)) {
227 			*try = *(fte->feeder);
228 			try->source = source;
229 			try->desc = trydesc;
230 			trydesc->out = fte->desc->out;
231 			trydesc->idx = fte->idx;
232 			ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
233 			if (ret != NULL)
234 				return ret;
235 		}
236 	}
237 	free(try, M_DEVBUF);
238 	free(trydesc, M_DEVBUF);
239 	/* printf("giving up %s...\n", source->name); */
240 	return NULL;
241 }
242 
243 u_int32_t
244 chn_fmtchain(pcm_channel *c, u_int32_t *to)
245 {
246 	pcm_feeder *try, *stop;
247 	int max;
248 
249 	stop = c->feeder;
250 	try = NULL;
251 	max = 0;
252 	while (try == NULL && max < 8) {
253 		try = feeder_fmtchain(to, c->feeder, stop, max);
254 		max++;
255 	}
256 	if (try == NULL)
257 		return 0;
258 	c->feeder = try;
259 	c->align = 0;
260 #ifdef FEEDER_DEBUG
261 	printf("chain: ");
262 #endif
263 	while (try && (try != stop)) {
264 #ifdef FEEDER_DEBUG
265 		printf("%s [%d]", try->name, try->desc->idx);
266 		if (try->source)
267 			printf(" -> ");
268 #endif
269 		if (try->init)
270 			try->init(try);
271 		if (try->align > 0)
272 			c->align += try->align;
273 		else if (try->align < 0 && c->align < -try->align)
274 			c->align = -try->align;
275 		try = try->source;
276 	}
277 #ifdef FEEDER_DEBUG
278 	printf("%s [%d]\n", try->name, try->desc->idx);
279 #endif
280 	return c->feeder->desc->out;
281 }
282 
283 /*****************************************************************************/
284 
285 static int
286 feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream)
287 {
288 	int ret, s;
289 
290 	KASSERT(count, ("feed_root: count == 0"));
291 	count &= ~((1 << ch->align) - 1);
292 	KASSERT(count, ("feed_root: aligned count == 0 (align = %d)", ch->align));
293 
294 	s = spltty();
295 	count = min(count, stream->uio_resid);
296 	if (count) {
297 		ret = uiomove(buffer, count, stream);
298 		KASSERT(ret == 0, ("feed_root: uiomove failed"));
299 	}
300 	splx(s);
301 
302 	return count;
303 }
304 static pcm_feeder feeder_root = {
305 	"root",
306 	0,
307 	NULL,
308 	NULL,
309 	NULL,
310 	NULL,
311 	feed_root,
312 };
313 SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root);
314 
315 
316 
317 
318