xref: /dragonfly/sys/dev/sound/pcm/feeder.c (revision af79c6e5)
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: src/sys/dev/sound/pcm/feeder.c,v 1.8.2.9 2003/02/08 01:43:07 orion Exp $
27  * $DragonFly: src/sys/dev/sound/pcm/feeder.c,v 1.3 2003/11/15 21:05:42 dillon Exp $
28  */
29 
30 #include <dev/sound/pcm/sound.h>
31 
32 #include "feeder_if.h"
33 
34 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/feeder.c,v 1.3 2003/11/15 21:05:42 dillon Exp $");
35 
36 MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
37 
38 #define MAXFEEDERS 	256
39 #undef FEEDER_DEBUG
40 
41 struct feedertab_entry {
42 	SLIST_ENTRY(feedertab_entry) link;
43 	struct feeder_class *feederclass;
44 	struct pcm_feederdesc *desc;
45 
46 	int idx;
47 };
48 static SLIST_HEAD(, feedertab_entry) feedertab;
49 
50 /*****************************************************************************/
51 
52 void
53 feeder_register(void *p)
54 {
55 	static int feedercnt = 0;
56 
57 	struct feeder_class *fc = p;
58 	struct feedertab_entry *fte;
59 	int i;
60 
61 	if (feedercnt == 0) {
62 		KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name));
63 
64 		SLIST_INIT(&feedertab);
65 		fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
66 		if (fte == NULL) {
67 			printf("can't allocate memory for root feeder\n");
68 			return;
69 		}
70 		fte->feederclass = fc;
71 		fte->desc = NULL;
72 		fte->idx = feedercnt;
73 		SLIST_INSERT_HEAD(&feedertab, fte, link);
74 		feedercnt++;
75 
76 		/* we've got our root feeder so don't veto pcm loading anymore */
77 		pcm_veto_load = 0;
78 
79 		return;
80 	}
81 
82 	KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name));
83 
84 	/* beyond this point failure is non-fatal but may result in some translations being unavailable */
85 	i = 0;
86 	while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) {
87 		/* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */
88 		fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
89 		if (fte == NULL) {
90 			printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out);
91 
92 			return;
93 		}
94 		fte->feederclass = fc;
95 		fte->desc = &fc->desc[i];
96 		fte->idx = feedercnt;
97 		fte->desc->idx = feedercnt;
98 		SLIST_INSERT_HEAD(&feedertab, fte, link);
99 		i++;
100 	}
101 	feedercnt++;
102 	if (feedercnt >= MAXFEEDERS)
103 		printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS);
104 }
105 
106 static void
107 feeder_unregisterall(void *p)
108 {
109 	struct feedertab_entry *fte, *next;
110 
111 	next = SLIST_FIRST(&feedertab);
112 	while (next != NULL) {
113 		fte = next;
114 		next = SLIST_NEXT(fte, link);
115 		free(fte, M_FEEDER);
116 	}
117 }
118 
119 static int
120 cmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m)
121 {
122 	return ((n->type == m->type) &&
123 		((n->in == 0) || (n->in == m->in)) &&
124 		((n->out == 0) || (n->out == m->out)) &&
125 		(n->flags == m->flags));
126 }
127 
128 static void
129 feeder_destroy(struct pcm_feeder *f)
130 {
131 	FEEDER_FREE(f);
132 	kobj_delete((kobj_t)f, M_FEEDER);
133 }
134 
135 static struct pcm_feeder *
136 feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc)
137 {
138 	struct pcm_feeder *f;
139 	int err;
140 
141 	f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_WAITOK | M_ZERO);
142 	if (f == NULL)
143 		return NULL;
144 
145 	f->align = fc->align;
146 	f->data = fc->data;
147 	f->source = NULL;
148 	f->parent = NULL;
149 	f->class = fc;
150 	f->desc = &(f->desc_static);
151 
152 	if (desc) {
153 		*(f->desc) = *desc;
154 	} else {
155 		f->desc->type = FEEDER_ROOT;
156 		f->desc->in = 0;
157 		f->desc->out = 0;
158 		f->desc->flags = 0;
159 		f->desc->idx = 0;
160 	}
161 
162 	err = FEEDER_INIT(f);
163 	if (err) {
164 		printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err);
165 		feeder_destroy(f);
166 
167 		return NULL;
168 	}
169 
170 	return f;
171 }
172 
173 struct feeder_class *
174 feeder_getclass(struct pcm_feederdesc *desc)
175 {
176 	struct feedertab_entry *fte;
177 
178 	SLIST_FOREACH(fte, &feedertab, link) {
179 		if ((desc == NULL) && (fte->desc == NULL))
180 			return fte->feederclass;
181 		if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc))
182 			return fte->feederclass;
183 	}
184 	return NULL;
185 }
186 
187 int
188 chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc)
189 {
190 	struct pcm_feeder *nf;
191 
192 	nf = feeder_create(fc, desc);
193 	if (nf == NULL)
194 		return ENOSPC;
195 
196 	nf->source = c->feeder;
197 
198 	if (nf->align > 0)
199 		c->align += nf->align;
200 	else if (nf->align < 0 && c->align < -nf->align)
201 		c->align = -nf->align;
202 
203 	c->feeder = nf;
204 
205 	return 0;
206 }
207 
208 int
209 chn_removefeeder(struct pcm_channel *c)
210 {
211 	struct pcm_feeder *f;
212 
213 	if (c->feeder == NULL)
214 		return -1;
215 	f = c->feeder;
216 	c->feeder = c->feeder->source;
217 	feeder_destroy(f);
218 
219 	return 0;
220 }
221 
222 struct pcm_feeder *
223 chn_findfeeder(struct pcm_channel *c, u_int32_t type)
224 {
225 	struct pcm_feeder *f;
226 
227 	f = c->feeder;
228 	while (f != NULL) {
229 		if (f->desc->type == type)
230 			return f;
231 		f = f->source;
232 	}
233 
234 	return NULL;
235 }
236 
237 static int
238 chainok(struct pcm_feeder *test, struct pcm_feeder *stop)
239 {
240 	u_int32_t visited[MAXFEEDERS / 32];
241 	u_int32_t idx, mask;
242 
243 	bzero(visited, sizeof(visited));
244 	while (test && (test != stop)) {
245 		idx = test->desc->idx;
246 		if (idx < 0)
247 			panic("bad idx %d", idx);
248 		if (idx >= MAXFEEDERS)
249 			panic("bad idx %d", idx);
250 		mask = 1 << (idx & 31);
251 		idx >>= 5;
252 		if (visited[idx] & mask)
253 			return 0;
254 		visited[idx] |= mask;
255 		test = test->source;
256 	}
257 
258 	return 1;
259 }
260 
261 static struct pcm_feeder *
262 feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth)
263 {
264 	struct feedertab_entry *fte;
265 	struct pcm_feeder *try, *ret;
266 
267 	/* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */
268 	if (fmtvalid(source->desc->out, to)) {
269 		/* printf("got it\n"); */
270 		return source;
271 	}
272 
273 	if (maxdepth < 0)
274 		return NULL;
275 
276 	SLIST_FOREACH(fte, &feedertab, link) {
277 		if (fte->desc == NULL)
278 			continue;
279 		if (fte->desc->type != FEEDER_FMT)
280 			continue;
281 		if (fte->desc->in == source->desc->out) {
282 			try = feeder_create(fte->feederclass, fte->desc);
283 			if (try) {
284 				try->source = source;
285 				ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
286 				if (ret != NULL)
287 					return ret;
288 				feeder_destroy(try);
289 			}
290 		}
291 	}
292 	/* printf("giving up %s...\n", source->class->name); */
293 
294 	return NULL;
295 }
296 
297 u_int32_t
298 chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
299 {
300 	struct pcm_feeder *try, *del, *stop;
301 	u_int32_t tmpfrom[2], best, *from;
302 	int i, max, bestmax;
303 
304 	KASSERT(c != NULL, ("c == NULL"));
305 	KASSERT(c->feeder != NULL, ("c->feeder == NULL"));
306 	KASSERT(to != NULL, ("to == NULL"));
307 	KASSERT(to[0] != 0, ("to[0] == 0"));
308 
309 	stop = c->feeder;
310 
311 	if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) {
312 		from = chn_getcaps(c)->fmtlist;
313 	} else {
314 		tmpfrom[0] = c->feeder->desc->out;
315 		tmpfrom[1] = 0;
316 		from = tmpfrom;
317 	}
318 
319 	i = 0;
320 	best = 0;
321 	bestmax = 100;
322 	while (from[i] != 0) {
323 		c->feeder->desc->out = from[i];
324 		try = NULL;
325 		max = 0;
326 		while (try == NULL && max < 8) {
327 			try = feeder_fmtchain(to, c->feeder, stop, max);
328 			if (try == NULL)
329 				max++;
330 		}
331 		if (try != NULL && max < bestmax) {
332 			bestmax = max;
333 			best = from[i];
334 		}
335 		while (try != NULL && try != stop) {
336 			del = try;
337 			try = try->source;
338 			feeder_destroy(del);
339 		}
340 		i++;
341 	}
342 	if (best == 0)
343 		return 0;
344 
345 	c->feeder->desc->out = best;
346 	try = feeder_fmtchain(to, c->feeder, stop, bestmax);
347 	if (try == NULL)
348 		return 0;
349 
350 	c->feeder = try;
351 	c->align = 0;
352 #ifdef FEEDER_DEBUG
353 	printf("\n\nchain: ");
354 #endif
355 	while (try && (try != stop)) {
356 #ifdef FEEDER_DEBUG
357 		printf("%s [%d]", try->class->name, try->desc->idx);
358 		if (try->source)
359 			printf(" -> ");
360 #endif
361 		if (try->source)
362 			try->source->parent = try;
363 		if (try->align > 0)
364 			c->align += try->align;
365 		else if (try->align < 0 && c->align < -try->align)
366 			c->align = -try->align;
367 		try = try->source;
368 	}
369 #ifdef FEEDER_DEBUG
370 	printf("%s [%d]\n", try->class->name, try->desc->idx);
371 #endif
372 
373 	return (c->direction == PCMDIR_REC)? best : c->feeder->desc->out;
374 }
375 
376 /*****************************************************************************/
377 
378 static int
379 feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source)
380 {
381 	struct snd_dbuf *src = source;
382 	int l;
383 	u_int8_t x;
384 
385 	KASSERT(count > 0, ("feed_root: count == 0"));
386 	/* count &= ~((1 << ch->align) - 1); */
387 	KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align));
388 
389 	l = min(count, sndbuf_getready(src));
390 	sndbuf_dispose(src, buffer, l);
391 
392 	/* When recording only return as much data as available */
393 	if (ch->direction == PCMDIR_REC)
394 		return l;
395 
396 /*
397 	if (l < count)
398 		printf("appending %d bytes\n", count - l);
399 */
400 
401 	x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80;
402 	while (l < count)
403 		buffer[l++] = x;
404 
405 	return count;
406 }
407 
408 static kobj_method_t feeder_root_methods[] = {
409     	KOBJMETHOD(feeder_feed,		feed_root),
410 	{ 0, 0 }
411 };
412 static struct feeder_class feeder_root_class = {
413 	name:		"feeder_root",
414 	methods:	feeder_root_methods,
415 	size:		sizeof(struct pcm_feeder),
416 	align:		0,
417 	desc:		NULL,
418 	data:		NULL,
419 };
420 SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class);
421 SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);
422 
423 
424 
425 
426 
427