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