feeder.c revision 149950
1139749Simp/*-
2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
350724Scg * All rights reserved.
450724Scg *
550724Scg * Redistribution and use in source and binary forms, with or without
650724Scg * modification, are permitted provided that the following conditions
750724Scg * are met:
850724Scg * 1. Redistributions of source code must retain the above copyright
950724Scg *    notice, this list of conditions and the following disclaimer.
1050724Scg * 2. Redistributions in binary form must reproduce the above copyright
1150724Scg *    notice, this list of conditions and the following disclaimer in the
1250724Scg *    documentation and/or other materials provided with the distribution.
1350724Scg *
1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2450724Scg * SUCH DAMAGE.
2550724Scg */
2650724Scg
2753465Scg#include <dev/sound/pcm/sound.h>
2850724Scg
2970134Scg#include "feeder_if.h"
3070134Scg
3182180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder.c 149950 2005-09-10 17:47:39Z netchild $");
3282180Scg
3370134ScgMALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
3470134Scg
3566308Scg#define MAXFEEDERS 	256
3665645Scg#undef FEEDER_DEBUG
3750724Scg
3864881Scgstruct feedertab_entry {
3964881Scg	SLIST_ENTRY(feedertab_entry) link;
4070134Scg	struct feeder_class *feederclass;
4164881Scg	struct pcm_feederdesc *desc;
4264881Scg
4364881Scg	int idx;
4464881Scg};
4564881Scgstatic SLIST_HEAD(, feedertab_entry) feedertab;
4664881Scg
4750724Scg/*****************************************************************************/
4850724Scg
4964881Scgvoid
5064881Scgfeeder_register(void *p)
5164881Scg{
5289834Scg	static int feedercnt = 0;
5389834Scg
5470134Scg	struct feeder_class *fc = p;
5564881Scg	struct feedertab_entry *fte;
5664881Scg	int i;
5764881Scg
5864881Scg	if (feedercnt == 0) {
5989834Scg		KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name));
6089834Scg
6164881Scg		SLIST_INIT(&feedertab);
62111909Sorion		fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO);
6389834Scg		if (fte == NULL) {
6497274Sbde			printf("can't allocate memory for root feeder: %s\n",
6597274Sbde			    fc->name);
6689834Scg
6789834Scg			return;
6889834Scg		}
6970134Scg		fte->feederclass = fc;
7064881Scg		fte->desc = NULL;
7164881Scg		fte->idx = feedercnt;
7264881Scg		SLIST_INSERT_HEAD(&feedertab, fte, link);
7364881Scg		feedercnt++;
7489834Scg
7589834Scg		/* we've got our root feeder so don't veto pcm loading anymore */
7689834Scg		pcm_veto_load = 0;
7789834Scg
7864881Scg		return;
7964881Scg	}
8064881Scg
8189834Scg	KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name));
8289834Scg
8389834Scg	/* beyond this point failure is non-fatal but may result in some translations being unavailable */
8464881Scg	i = 0;
8570134Scg	while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) {
8675355Scg		/* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */
87111909Sorion		fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO);
8889834Scg		if (fte == NULL) {
8989834Scg			printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out);
9089834Scg
9189834Scg			return;
9289834Scg		}
9370134Scg		fte->feederclass = fc;
9470134Scg		fte->desc = &fc->desc[i];
9564881Scg		fte->idx = feedercnt;
9664881Scg		fte->desc->idx = feedercnt;
9764881Scg		SLIST_INSERT_HEAD(&feedertab, fte, link);
9864881Scg		i++;
9964881Scg	}
10064881Scg	feedercnt++;
10164881Scg	if (feedercnt >= MAXFEEDERS)
10289834Scg		printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS);
10364881Scg}
10464881Scg
10574363Scgstatic void
10674363Scgfeeder_unregisterall(void *p)
10774363Scg{
10874363Scg	struct feedertab_entry *fte, *next;
10974363Scg
11074363Scg	next = SLIST_FIRST(&feedertab);
11174363Scg	while (next != NULL) {
11274363Scg		fte = next;
11374363Scg		next = SLIST_NEXT(fte, link);
11474363Scg		free(fte, M_FEEDER);
11574363Scg	}
11674363Scg}
11774363Scg
11850724Scgstatic int
11964881Scgcmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m)
12050724Scg{
12166308Scg	return ((n->type == m->type) &&
12266308Scg		((n->in == 0) || (n->in == m->in)) &&
12366308Scg		((n->out == 0) || (n->out == m->out)) &&
12466308Scg		(n->flags == m->flags));
12550724Scg}
12650724Scg
12770134Scgstatic void
12874763Scgfeeder_destroy(struct pcm_feeder *f)
12950724Scg{
13070134Scg	FEEDER_FREE(f);
13170134Scg	kobj_delete((kobj_t)f, M_FEEDER);
13270134Scg}
13364881Scg
13474763Scgstatic struct pcm_feeder *
13570134Scgfeeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc)
13670134Scg{
13774763Scg	struct pcm_feeder *f;
13870134Scg	int err;
13970134Scg
140111909Sorion	f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO);
14189834Scg	if (f == NULL)
14289834Scg		return NULL;
14389834Scg
14470134Scg	f->align = fc->align;
14589834Scg	f->data = fc->data;
14689834Scg	f->source = NULL;
14789834Scg	f->parent = NULL;
14889834Scg	f->class = fc;
14989834Scg	f->desc = &(f->desc_static);
15089834Scg
15189834Scg	if (desc) {
15270134Scg		*(f->desc) = *desc;
15389834Scg	} else {
15470134Scg		f->desc->type = FEEDER_ROOT;
15570134Scg		f->desc->in = 0;
15670134Scg		f->desc->out = 0;
15770134Scg		f->desc->flags = 0;
15870134Scg		f->desc->idx = 0;
15964881Scg	}
16089834Scg
16170134Scg	err = FEEDER_INIT(f);
16270134Scg	if (err) {
16375319Scg		printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err);
16470134Scg		feeder_destroy(f);
16589834Scg
16670134Scg		return NULL;
16789834Scg	}
16889834Scg
16989834Scg	return f;
17050724Scg}
17150724Scg
17270134Scgstruct feeder_class *
17370134Scgfeeder_getclass(struct pcm_feederdesc *desc)
17450724Scg{
17564881Scg	struct feedertab_entry *fte;
17650724Scg
17764881Scg	SLIST_FOREACH(fte, &feedertab, link) {
17870134Scg		if ((desc == NULL) && (fte->desc == NULL))
17970134Scg			return fte->feederclass;
18070134Scg		if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc))
18170134Scg			return fte->feederclass;
18264881Scg	}
18364881Scg	return NULL;
18450724Scg}
18550724Scg
18650724Scgint
18774763Scgchn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc)
18866308Scg{
18974763Scg	struct pcm_feeder *nf;
19070134Scg
19170134Scg	nf = feeder_create(fc, desc);
19270134Scg	if (nf == NULL)
19389834Scg		return ENOSPC;
19466308Scg
19570134Scg	nf->source = c->feeder;
19666308Scg
197117307Scg	/* XXX we should use the lowest common denominator for align */
19866308Scg	if (nf->align > 0)
19966308Scg		c->align += nf->align;
20066308Scg	else if (nf->align < 0 && c->align < -nf->align)
20166308Scg		c->align = -nf->align;
202117307Scg	if (c->feeder != NULL)
203117307Scg		c->feeder->parent = nf;
20466308Scg	c->feeder = nf;
20566308Scg
20666308Scg	return 0;
20766308Scg}
20866308Scg
20966308Scgint
21074763Scgchn_removefeeder(struct pcm_channel *c)
21150724Scg{
21274763Scg	struct pcm_feeder *f;
21350724Scg
21470134Scg	if (c->feeder == NULL)
21564881Scg		return -1;
21670134Scg	f = c->feeder;
21770134Scg	c->feeder = c->feeder->source;
21870134Scg	feeder_destroy(f);
21989834Scg
22064881Scg	return 0;
22150724Scg}
22250724Scg
22374763Scgstruct pcm_feeder *
22474763Scgchn_findfeeder(struct pcm_channel *c, u_int32_t type)
22566308Scg{
22674763Scg	struct pcm_feeder *f;
22766308Scg
22866308Scg	f = c->feeder;
22966308Scg	while (f != NULL) {
23066308Scg		if (f->desc->type == type)
23166308Scg			return f;
23266308Scg		f = f->source;
23366308Scg	}
23489834Scg
23566308Scg	return NULL;
23666308Scg}
23766308Scg
23850724Scgstatic int
23974763Scgchainok(struct pcm_feeder *test, struct pcm_feeder *stop)
24050724Scg{
24164881Scg	u_int32_t visited[MAXFEEDERS / 32];
24264881Scg	u_int32_t idx, mask;
24364881Scg
24464881Scg	bzero(visited, sizeof(visited));
24564881Scg	while (test && (test != stop)) {
24664881Scg		idx = test->desc->idx;
24764881Scg		if (idx < 0)
24864881Scg			panic("bad idx %d", idx);
24964881Scg		if (idx >= MAXFEEDERS)
25064881Scg			panic("bad idx %d", idx);
25164881Scg		mask = 1 << (idx & 31);
25264881Scg		idx >>= 5;
25364881Scg		if (visited[idx] & mask)
25464881Scg			return 0;
25564881Scg		visited[idx] |= mask;
25664881Scg		test = test->source;
25764881Scg	}
25889834Scg
25964881Scg	return 1;
26050724Scg}
26150724Scg
26274763Scgstatic struct pcm_feeder *
26374763Scgfeeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth)
26450724Scg{
26564881Scg	struct feedertab_entry *fte;
26674763Scg	struct pcm_feeder *try, *ret;
26764881Scg
26875319Scg	/* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */
26964881Scg	if (fmtvalid(source->desc->out, to)) {
27064881Scg		/* printf("got it\n"); */
27164881Scg		return source;
27264881Scg	}
27364881Scg
27464881Scg	if (maxdepth < 0)
27564881Scg		return NULL;
27664881Scg
27764881Scg	SLIST_FOREACH(fte, &feedertab, link) {
27875319Scg		if (fte->desc == NULL)
27996928Speter			continue;
28075319Scg		if (fte->desc->type != FEEDER_FMT)
28196928Speter			continue;
28275319Scg		if (fte->desc->in == source->desc->out) {
28375319Scg			try = feeder_create(fte->feederclass, fte->desc);
28475319Scg			if (try) {
28575319Scg				try->source = source;
28675319Scg				ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
28775319Scg				if (ret != NULL)
28875319Scg					return ret;
28975319Scg				feeder_destroy(try);
29075319Scg			}
29164881Scg		}
29264881Scg	}
29375319Scg	/* printf("giving up %s...\n", source->class->name); */
29489834Scg
29564881Scg	return NULL;
29650724Scg}
29750724Scg
29864881Scgu_int32_t
29974763Scgchn_fmtchain(struct pcm_channel *c, u_int32_t *to)
30064881Scg{
30189686Scg	struct pcm_feeder *try, *del, *stop;
30289686Scg	u_int32_t tmpfrom[2], best, *from;
30389686Scg	int i, max, bestmax;
30464881Scg
30589686Scg	KASSERT(c != NULL, ("c == NULL"));
30689686Scg	KASSERT(c->feeder != NULL, ("c->feeder == NULL"));
30789686Scg	KASSERT(to != NULL, ("to == NULL"));
30889686Scg	KASSERT(to[0] != 0, ("to[0] == 0"));
30989686Scg
31064881Scg	stop = c->feeder;
31189686Scg
31289686Scg	if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) {
31389686Scg		from = chn_getcaps(c)->fmtlist;
31489686Scg	} else {
31589686Scg		tmpfrom[0] = c->feeder->desc->out;
31689686Scg		tmpfrom[1] = 0;
31789686Scg		from = tmpfrom;
31865645Scg	}
31989686Scg
32089686Scg	i = 0;
32189686Scg	best = 0;
32289686Scg	bestmax = 100;
323149950Snetchild	while (from[i] != 0)
324149950Snetchild		i++;
325149950Snetchild	while (i > 0) {
326149950Snetchild		i--;
32789686Scg		c->feeder->desc->out = from[i];
32889686Scg		try = NULL;
32989686Scg		max = 0;
33089686Scg		while (try == NULL && max < 8) {
33189686Scg			try = feeder_fmtchain(to, c->feeder, stop, max);
33289686Scg			if (try == NULL)
33389686Scg				max++;
33489686Scg		}
33589686Scg		if (try != NULL && max < bestmax) {
33689686Scg			bestmax = max;
33789686Scg			best = from[i];
33889686Scg		}
33989686Scg		while (try != NULL && try != stop) {
34089686Scg			del = try;
34189686Scg			try = try->source;
34289686Scg			feeder_destroy(del);
34389686Scg		}
34489686Scg	}
34589686Scg	if (best == 0)
34689686Scg		return 0;
34789686Scg
34889686Scg	c->feeder->desc->out = best;
34989686Scg	try = feeder_fmtchain(to, c->feeder, stop, bestmax);
35064881Scg	if (try == NULL)
35164881Scg		return 0;
35289686Scg
35364881Scg	c->feeder = try;
35464881Scg	c->align = 0;
35565645Scg#ifdef FEEDER_DEBUG
35689686Scg	printf("\n\nchain: ");
35765645Scg#endif
35864881Scg	while (try && (try != stop)) {
35965645Scg#ifdef FEEDER_DEBUG
36075319Scg		printf("%s [%d]", try->class->name, try->desc->idx);
36165645Scg		if (try->source)
36265645Scg			printf(" -> ");
36365645Scg#endif
36489686Scg		if (try->source)
36589686Scg			try->source->parent = try;
36664881Scg		if (try->align > 0)
36764881Scg			c->align += try->align;
36864881Scg		else if (try->align < 0 && c->align < -try->align)
36964881Scg			c->align = -try->align;
37064881Scg		try = try->source;
37164881Scg	}
37265645Scg#ifdef FEEDER_DEBUG
37375319Scg	printf("%s [%d]\n", try->class->name, try->desc->idx);
37465645Scg#endif
37589834Scg
376149950Snetchild	if (c->direction == PCMDIR_REC) {
377149950Snetchild		try = c->feeder;
378149950Snetchild		while (try != NULL) {
379149950Snetchild			if (try->desc->type == FEEDER_ROOT)
380149950Snetchild				return try->desc->out;
381149950Snetchild			try = try->source;
382149950Snetchild		}
383149950Snetchild		return best;
384149950Snetchild	} else
385149950Snetchild		return c->feeder->desc->out;
38664881Scg}
38766308Scg
388117307Scgvoid
389117307Scgfeeder_printchain(struct pcm_feeder *head)
390117307Scg{
391117307Scg	struct pcm_feeder *f;
392117307Scg
393117307Scg	printf("feeder chain (head @%p)\n", head);
394117307Scg	f = head;
395117307Scg	while (f != NULL) {
396117307Scg		printf("%s/%d @ %p\n", f->class->name, f->desc->idx, f);
397117307Scg		f = f->source;
398117307Scg	}
399117307Scg	printf("[end]\n\n");
400117307Scg}
401117307Scg
40266308Scg/*****************************************************************************/
40366308Scg
40466308Scgstatic int
40574763Scgfeed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source)
40666308Scg{
40774763Scg	struct snd_dbuf *src = source;
40874763Scg	int l;
40974763Scg	u_int8_t x;
41066308Scg
41174763Scg	KASSERT(count > 0, ("feed_root: count == 0"));
41274763Scg	/* count &= ~((1 << ch->align) - 1); */
41374763Scg	KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align));
41466308Scg
41574763Scg	l = min(count, sndbuf_getready(src));
41674763Scg	sndbuf_dispose(src, buffer, l);
41766308Scg
418110287Sorion	/* When recording only return as much data as available */
419110287Sorion	if (ch->direction == PCMDIR_REC)
420110287Sorion		return l;
421110287Sorion
42274763Scg/*
42374763Scg	if (l < count)
42474763Scg		printf("appending %d bytes\n", count - l);
42574763Scg*/
42674763Scg
42774763Scg	x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80;
42874763Scg	while (l < count)
42974763Scg		buffer[l++] = x;
43074763Scg
43166308Scg	return count;
43266308Scg}
43370134Scg
43470134Scgstatic kobj_method_t feeder_root_methods[] = {
43570134Scg    	KOBJMETHOD(feeder_feed,		feed_root),
43670134Scg	{ 0, 0 }
43766308Scg};
43870134Scgstatic struct feeder_class feeder_root_class = {
439118473Sdds	.name =		"feeder_root",
440118473Sdds	.methods =	feeder_root_methods,
441118473Sdds	.size =		sizeof(struct pcm_feeder),
442118473Sdds	.align =	0,
443118473Sdds	.desc =		NULL,
444118473Sdds	.data =		NULL,
44570134Scg};
44670134ScgSYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class);
44774363ScgSYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);
44866308Scg
44966308Scg
45066308Scg
45166308Scg
45270134Scg
453