feeder.c revision 119853
1269369Sbr/*
2269369Sbr * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3269369Sbr * All rights reserved.
4269369Sbr *
5269369Sbr * Redistribution and use in source and binary forms, with or without
6269369Sbr * modification, are permitted provided that the following conditions
7269369Sbr * are met:
8269369Sbr * 1. Redistributions of source code must retain the above copyright
9269369Sbr *    notice, this list of conditions and the following disclaimer.
10269369Sbr * 2. Redistributions in binary form must reproduce the above copyright
11269369Sbr *    notice, this list of conditions and the following disclaimer in the
12269369Sbr *    documentation and/or other materials provided with the distribution.
13269369Sbr *
14269369Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15269369Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16269369Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17269369Sbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18269369Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19269369Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20269369Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21269369Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22269369Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23269369Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24269369Sbr * SUCH DAMAGE.
25269369Sbr */
26269369Sbr
27269369Sbr#include <dev/sound/pcm/sound.h>
28269369Sbr
29269369Sbr#include "feeder_if.h"
30269369Sbr
31269369SbrSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder.c 119853 2003-09-07 16:28:03Z cg $");
32269369Sbr
33269369SbrMALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
34269369Sbr
35269369Sbr#define MAXFEEDERS 	256
36269369Sbr#undef FEEDER_DEBUG
37269369Sbr
38269369Sbrstruct feedertab_entry {
39269369Sbr	SLIST_ENTRY(feedertab_entry) link;
40269369Sbr	struct feeder_class *feederclass;
41269369Sbr	struct pcm_feederdesc *desc;
42269369Sbr
43269369Sbr	int idx;
44269369Sbr};
45269369Sbrstatic SLIST_HEAD(, feedertab_entry) feedertab;
46269369Sbr
47269369Sbr/*****************************************************************************/
48269369Sbr
49269369Sbrvoid
50269369Sbrfeeder_register(void *p)
51269369Sbr{
52269369Sbr	static int feedercnt = 0;
53269369Sbr
54269369Sbr	struct feeder_class *fc = p;
55269369Sbr	struct feedertab_entry *fte;
56269369Sbr	int i;
57269369Sbr
58269369Sbr	if (feedercnt == 0) {
59269369Sbr		KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name));
60269369Sbr
61269369Sbr		SLIST_INIT(&feedertab);
62269369Sbr		fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO);
63269369Sbr		if (fte == NULL) {
64269369Sbr			printf("can't allocate memory for root feeder: %s\n",
65269369Sbr			    fc->name);
66269369Sbr
67269369Sbr			return;
68269369Sbr		}
69269369Sbr		fte->feederclass = fc;
70269369Sbr		fte->desc = NULL;
71269369Sbr		fte->idx = feedercnt;
72269369Sbr		SLIST_INSERT_HEAD(&feedertab, fte, link);
73269369Sbr		feedercnt++;
74269369Sbr
75269369Sbr		/* we've got our root feeder so don't veto pcm loading anymore */
76269369Sbr		pcm_veto_load = 0;
77269369Sbr
78269369Sbr		return;
79269369Sbr	}
80269369Sbr
81269369Sbr	KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name));
82269369Sbr
83269369Sbr	/* beyond this point failure is non-fatal but may result in some translations being unavailable */
84269369Sbr	i = 0;
85269369Sbr	while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) {
86269369Sbr		/* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */
87269369Sbr		fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO);
88269369Sbr		if (fte == NULL) {
89269369Sbr			printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out);
90269369Sbr
91269369Sbr			return;
92279542Shselasky		}
93279542Shselasky		fte->feederclass = fc;
94279542Shselasky		fte->desc = &fc->desc[i];
95269369Sbr		fte->idx = feedercnt;
96269369Sbr		fte->desc->idx = feedercnt;
97269369Sbr		SLIST_INSERT_HEAD(&feedertab, fte, link);
98269369Sbr		i++;
99269369Sbr	}
100269369Sbr	feedercnt++;
101269369Sbr	if (feedercnt >= MAXFEEDERS)
102269369Sbr		printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS);
103269369Sbr}
104269369Sbr
105269369Sbrstatic void
106269369Sbrfeeder_unregisterall(void *p)
107269369Sbr{
108269369Sbr	struct feedertab_entry *fte, *next;
109269369Sbr
110269369Sbr	next = SLIST_FIRST(&feedertab);
111269369Sbr	while (next != NULL) {
112269369Sbr		fte = next;
113269369Sbr		next = SLIST_NEXT(fte, link);
114269369Sbr		free(fte, M_FEEDER);
115269369Sbr	}
116269369Sbr}
117269369Sbr
118269369Sbrstatic int
119269369Sbrcmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m)
120269369Sbr{
121269369Sbr	return ((n->type == m->type) &&
122269369Sbr		((n->in == 0) || (n->in == m->in)) &&
123269369Sbr		((n->out == 0) || (n->out == m->out)) &&
124269369Sbr		(n->flags == m->flags));
125269369Sbr}
126269369Sbr
127269369Sbrstatic void
128269369Sbrfeeder_destroy(struct pcm_feeder *f)
129269369Sbr{
130269369Sbr	FEEDER_FREE(f);
131269369Sbr	kobj_delete((kobj_t)f, M_FEEDER);
132269369Sbr}
133269369Sbr
134269369Sbrstatic struct pcm_feeder *
135269369Sbrfeeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc)
136269369Sbr{
137269369Sbr	struct pcm_feeder *f;
138269369Sbr	int err;
139269369Sbr
140269369Sbr	f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO);
141269369Sbr	if (f == NULL)
142269369Sbr		return NULL;
143269369Sbr
144269369Sbr	f->align = fc->align;
145269369Sbr	f->data = fc->data;
146269369Sbr	f->source = NULL;
147269369Sbr	f->parent = NULL;
148269369Sbr	f->class = fc;
149269369Sbr	f->desc = &(f->desc_static);
150269369Sbr
151269369Sbr	if (desc) {
152269369Sbr		*(f->desc) = *desc;
153269369Sbr	} else {
154269369Sbr		f->desc->type = FEEDER_ROOT;
155269369Sbr		f->desc->in = 0;
156269369Sbr		f->desc->out = 0;
157269369Sbr		f->desc->flags = 0;
158269369Sbr		f->desc->idx = 0;
159269369Sbr	}
160269369Sbr
161269369Sbr	err = FEEDER_INIT(f);
162269369Sbr	if (err) {
163269369Sbr		printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err);
164269369Sbr		feeder_destroy(f);
165269369Sbr
166269369Sbr		return NULL;
167269369Sbr	}
168269369Sbr
169269369Sbr	return f;
170269369Sbr}
171269369Sbr
172269369Sbrstruct feeder_class *
173269369Sbrfeeder_getclass(struct pcm_feederdesc *desc)
174269369Sbr{
175269369Sbr	struct feedertab_entry *fte;
176269369Sbr
177269369Sbr	SLIST_FOREACH(fte, &feedertab, link) {
178269369Sbr		if ((desc == NULL) && (fte->desc == NULL))
179269369Sbr			return fte->feederclass;
180269369Sbr		if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc))
181269369Sbr			return fte->feederclass;
182269369Sbr	}
183269369Sbr	return NULL;
184269369Sbr}
185269369Sbr
186269369Sbrint
187269369Sbrchn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc)
188269369Sbr{
189269369Sbr	struct pcm_feeder *nf;
190269369Sbr
191269369Sbr	nf = feeder_create(fc, desc);
192269369Sbr	if (nf == NULL)
193269369Sbr		return ENOSPC;
194269369Sbr
195269369Sbr	nf->source = c->feeder;
196269369Sbr
197269369Sbr	/* XXX we should use the lowest common denominator for align */
198269369Sbr	if (nf->align > 0)
199269369Sbr		c->align += nf->align;
200269369Sbr	else if (nf->align < 0 && c->align < -nf->align)
201269369Sbr		c->align = -nf->align;
202269369Sbr	if (c->feeder != NULL)
203269369Sbr		c->feeder->parent = nf;
204269369Sbr	c->feeder = nf;
205269369Sbr
206269369Sbr	return 0;
207269369Sbr}
208269369Sbr
209269369Sbrint
210279542Shselaskychn_removefeeder(struct pcm_channel *c)
211269369Sbr{
212269369Sbr	struct pcm_feeder *f;
213269369Sbr
214269369Sbr	if (c->feeder == NULL)
215269369Sbr		return -1;
216269369Sbr	f = c->feeder;
217269369Sbr	c->feeder = c->feeder->source;
218269369Sbr	feeder_destroy(f);
219269369Sbr
220269369Sbr	return 0;
221269369Sbr}
222279542Shselasky
223269369Sbrstruct pcm_feeder *
224279542Shselaskychn_findfeeder(struct pcm_channel *c, u_int32_t type)
225269369Sbr{
226269369Sbr	struct pcm_feeder *f;
227269369Sbr
228269369Sbr	f = c->feeder;
229269369Sbr	while (f != NULL) {
230269369Sbr		if (f->desc->type == type)
231269369Sbr			return f;
232269369Sbr		f = f->source;
233269369Sbr	}
234279542Shselasky
235279542Shselasky	return NULL;
236279542Shselasky}
237279542Shselasky
238279542Shselaskystatic int
239269369Sbrchainok(struct pcm_feeder *test, struct pcm_feeder *stop)
240279542Shselasky{
241269369Sbr	u_int32_t visited[MAXFEEDERS / 32];
242279544Shselasky	u_int32_t idx, mask;
243279542Shselasky
244279542Shselasky	bzero(visited, sizeof(visited));
245279542Shselasky	while (test && (test != stop)) {
246279542Shselasky		idx = test->desc->idx;
247279542Shselasky		if (idx < 0)
248269369Sbr			panic("bad idx %d", idx);
249269369Sbr		if (idx >= MAXFEEDERS)
250279542Shselasky			panic("bad idx %d", idx);
251279542Shselasky		mask = 1 << (idx & 31);
252269369Sbr		idx >>= 5;
253269369Sbr		if (visited[idx] & mask)
254279542Shselasky			return 0;
255279542Shselasky		visited[idx] |= mask;
256269369Sbr		test = test->source;
257269369Sbr	}
258269369Sbr
259279542Shselasky	return 1;
260279542Shselasky}
261269369Sbr
262279542Shselaskystatic struct pcm_feeder *
263269369Sbrfeeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth)
264279542Shselasky{
265279542Shselasky	struct feedertab_entry *fte;
266269369Sbr	struct pcm_feeder *try, *ret;
267269369Sbr
268269369Sbr	/* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */
269279542Shselasky	if (fmtvalid(source->desc->out, to)) {
270269369Sbr		/* printf("got it\n"); */
271269369Sbr		return source;
272279542Shselasky	}
273269369Sbr
274269369Sbr	if (maxdepth < 0)
275269369Sbr		return NULL;
276279542Shselasky
277269369Sbr	SLIST_FOREACH(fte, &feedertab, link) {
278279542Shselasky		if (fte->desc == NULL)
279279542Shselasky			continue;
280279542Shselasky		if (fte->desc->type != FEEDER_FMT)
281269369Sbr			continue;
282279542Shselasky		if (fte->desc->in == source->desc->out) {
283279542Shselasky			try = feeder_create(fte->feederclass, fte->desc);
284279542Shselasky			if (try) {
285269369Sbr				try->source = source;
286269369Sbr				ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
287269369Sbr				if (ret != NULL)
288269369Sbr					return ret;
289269369Sbr				feeder_destroy(try);
290279542Shselasky			}
291279542Shselasky		}
292269369Sbr	}
293269369Sbr	/* printf("giving up %s...\n", source->class->name); */
294279542Shselasky
295279542Shselasky	return NULL;
296279542Shselasky}
297279542Shselasky
298279542Shselaskyu_int32_t
299279542Shselaskychn_fmtchain(struct pcm_channel *c, u_int32_t *to)
300279542Shselasky{
301269369Sbr	struct pcm_feeder *try, *del, *stop;
302279542Shselasky	u_int32_t tmpfrom[2], best, *from;
303279542Shselasky	int i, max, bestmax;
304279542Shselasky
305269369Sbr	KASSERT(c != NULL, ("c == NULL"));
306279542Shselasky	KASSERT(c->feeder != NULL, ("c->feeder == NULL"));
307269369Sbr	KASSERT(to != NULL, ("to == NULL"));
308279542Shselasky	KASSERT(to[0] != 0, ("to[0] == 0"));
309269369Sbr
310269369Sbr	stop = c->feeder;
311269369Sbr
312269369Sbr	if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) {
313269369Sbr		from = chn_getcaps(c)->fmtlist;
314269369Sbr	} else {
315269369Sbr		tmpfrom[0] = c->feeder->desc->out;
316279542Shselasky		tmpfrom[1] = 0;
317279542Shselasky		from = tmpfrom;
318269369Sbr	}
319269369Sbr
320	i = 0;
321	best = 0;
322	bestmax = 100;
323	while (from[i] != 0) {
324		c->feeder->desc->out = from[i];
325		try = NULL;
326		max = 0;
327		while (try == NULL && max < 8) {
328			try = feeder_fmtchain(to, c->feeder, stop, max);
329			if (try == NULL)
330				max++;
331		}
332		if (try != NULL && max < bestmax) {
333			bestmax = max;
334			best = from[i];
335		}
336		while (try != NULL && try != stop) {
337			del = try;
338			try = try->source;
339			feeder_destroy(del);
340		}
341		i++;
342	}
343	if (best == 0)
344		return 0;
345
346	c->feeder->desc->out = best;
347	try = feeder_fmtchain(to, c->feeder, stop, bestmax);
348	if (try == NULL)
349		return 0;
350
351	c->feeder = try;
352	c->align = 0;
353#ifdef FEEDER_DEBUG
354	printf("\n\nchain: ");
355#endif
356	while (try && (try != stop)) {
357#ifdef FEEDER_DEBUG
358		printf("%s [%d]", try->class->name, try->desc->idx);
359		if (try->source)
360			printf(" -> ");
361#endif
362		if (try->source)
363			try->source->parent = try;
364		if (try->align > 0)
365			c->align += try->align;
366		else if (try->align < 0 && c->align < -try->align)
367			c->align = -try->align;
368		try = try->source;
369	}
370#ifdef FEEDER_DEBUG
371	printf("%s [%d]\n", try->class->name, try->desc->idx);
372#endif
373
374	return (c->direction == PCMDIR_REC)? best : c->feeder->desc->out;
375}
376
377void
378feeder_printchain(struct pcm_feeder *head)
379{
380	struct pcm_feeder *f;
381
382	printf("feeder chain (head @%p)\n", head);
383	f = head;
384	while (f != NULL) {
385		printf("%s/%d @ %p\n", f->class->name, f->desc->idx, f);
386		f = f->source;
387	}
388	printf("[end]\n\n");
389}
390
391/*****************************************************************************/
392
393static int
394feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source)
395{
396	struct snd_dbuf *src = source;
397	int l;
398	u_int8_t x;
399
400	KASSERT(count > 0, ("feed_root: count == 0"));
401	/* count &= ~((1 << ch->align) - 1); */
402	KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align));
403
404	l = min(count, sndbuf_getready(src));
405	sndbuf_dispose(src, buffer, l);
406
407	/* When recording only return as much data as available */
408	if (ch->direction == PCMDIR_REC)
409		return l;
410
411/*
412	if (l < count)
413		printf("appending %d bytes\n", count - l);
414*/
415
416	x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80;
417	while (l < count)
418		buffer[l++] = x;
419
420	return count;
421}
422
423static kobj_method_t feeder_root_methods[] = {
424    	KOBJMETHOD(feeder_feed,		feed_root),
425	{ 0, 0 }
426};
427static struct feeder_class feeder_root_class = {
428	.name =		"feeder_root",
429	.methods =	feeder_root_methods,
430	.size =		sizeof(struct pcm_feeder),
431	.align =	0,
432	.desc =		NULL,
433	.data =		NULL,
434};
435SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class);
436SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);
437
438
439
440
441
442