feeder.c revision 111119
1145519Sdarrenr/*
2145510Sdarrenr * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3145510Sdarrenr * All rights reserved.
4255332Scy *
5145510Sdarrenr * Redistribution and use in source and binary forms, with or without
6145510Sdarrenr * modification, are permitted provided that the following conditions
7145510Sdarrenr * are met:
8145510Sdarrenr * 1. Redistributions of source code must retain the above copyright
9145510Sdarrenr *    notice, this list of conditions and the following disclaimer.
10145510Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright
11145510Sdarrenr *    notice, this list of conditions and the following disclaimer in the
12145510Sdarrenr *    documentation and/or other materials provided with the distribution.
13145510Sdarrenr *
14145510Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15145510Sdarrenr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16145510Sdarrenr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17145510Sdarrenr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18145510Sdarrenr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19145510Sdarrenr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20145510Sdarrenr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21145510Sdarrenr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22145510Sdarrenr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23145510Sdarrenr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24145510Sdarrenr * SUCH DAMAGE.
25145510Sdarrenr */
26145510Sdarrenr
27170268Sdarrenr#include <dev/sound/pcm/sound.h>
28145510Sdarrenr
29145510Sdarrenr#include "feeder_if.h"
30170268Sdarrenr
31145510SdarrenrSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder.c 111119 2003-02-19 05:47:46Z imp $");
32145510Sdarrenr
33145510SdarrenrMALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
34145510Sdarrenr
35145510Sdarrenr#define MAXFEEDERS 	256
36145510Sdarrenr#undef FEEDER_DEBUG
37369245Sgit2svn
38145510Sdarrenrstruct feedertab_entry {
39145510Sdarrenr	SLIST_ENTRY(feedertab_entry) link;
40145510Sdarrenr	struct feeder_class *feederclass;
41145510Sdarrenr	struct pcm_feederdesc *desc;
42145510Sdarrenr
43369245Sgit2svn	int idx;
44369245Sgit2svn};
45369245Sgit2svnstatic SLIST_HEAD(, feedertab_entry) feedertab;
46369245Sgit2svn
47369245Sgit2svn/*****************************************************************************/
48369245Sgit2svn
49369245Sgit2svnvoid
50369245Sgit2svnfeeder_register(void *p)
51369245Sgit2svn{
52369245Sgit2svn	static int feedercnt = 0;
53369245Sgit2svn
54369245Sgit2svn	struct feeder_class *fc = p;
55369245Sgit2svn	struct feedertab_entry *fte;
56369245Sgit2svn	int i;
57369245Sgit2svn
58369245Sgit2svn	if (feedercnt == 0) {
59145510Sdarrenr		KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name));
60145510Sdarrenr
61145510Sdarrenr		SLIST_INIT(&feedertab);
62145510Sdarrenr		fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
63255332Scy		if (fte == NULL) {
64255332Scy			printf("can't allocate memory for root feeder: %s\n",
65145510Sdarrenr			    fc->name);
66145510Sdarrenr
67255332Scy			return;
68255332Scy		}
69255332Scy		fte->feederclass = fc;
70145510Sdarrenr		fte->desc = NULL;
71145510Sdarrenr		fte->idx = feedercnt;
72353089Scy		SLIST_INSERT_HEAD(&feedertab, fte, link);
73255332Scy		feedercnt++;
74353092Scy
75255332Scy		/* we've got our root feeder so don't veto pcm loading anymore */
76353091Scy		pcm_veto_load = 0;
77255332Scy
78255332Scy		return;
79255332Scy	}
80145510Sdarrenr
81145510Sdarrenr	KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name));
82145510Sdarrenr
83145510Sdarrenr	/* beyond this point failure is non-fatal but may result in some translations being unavailable */
84255332Scy	i = 0;
85255332Scy	while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) {
86255332Scy		/* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */
87255332Scy		fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
88145510Sdarrenr		if (fte == NULL) {
89255332Scy			printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out);
90145510Sdarrenr
91145510Sdarrenr			return;
92145510Sdarrenr		}
93145510Sdarrenr		fte->feederclass = fc;
94255332Scy		fte->desc = &fc->desc[i];
95255332Scy		fte->idx = feedercnt;
96353083Scy		fte->desc->idx = feedercnt;
97145510Sdarrenr		SLIST_INSERT_HEAD(&feedertab, fte, link);
98145510Sdarrenr		i++;
99145510Sdarrenr	}
100145510Sdarrenr	feedercnt++;
101145510Sdarrenr	if (feedercnt >= MAXFEEDERS)
102145510Sdarrenr		printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS);
103145510Sdarrenr}
104145510Sdarrenr
105145510Sdarrenrstatic void
106145510Sdarrenrfeeder_unregisterall(void *p)
107145510Sdarrenr{
108145510Sdarrenr	struct feedertab_entry *fte, *next;
109145510Sdarrenr
110145510Sdarrenr	next = SLIST_FIRST(&feedertab);
111145510Sdarrenr	while (next != NULL) {
112145510Sdarrenr		fte = next;
113145510Sdarrenr		next = SLIST_NEXT(fte, link);
114145510Sdarrenr		free(fte, M_FEEDER);
115145510Sdarrenr	}
116145510Sdarrenr}
117145510Sdarrenr
118145510Sdarrenrstatic int
119145510Sdarrenrcmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m)
120145510Sdarrenr{
121145510Sdarrenr	return ((n->type == m->type) &&
122145510Sdarrenr		((n->in == 0) || (n->in == m->in)) &&
123145510Sdarrenr		((n->out == 0) || (n->out == m->out)) &&
124145510Sdarrenr		(n->flags == m->flags));
125145510Sdarrenr}
126170268Sdarrenr
127170268Sdarrenrstatic void
128170268Sdarrenrfeeder_destroy(struct pcm_feeder *f)
129145510Sdarrenr{
130145510Sdarrenr	FEEDER_FREE(f);
131145510Sdarrenr	kobj_delete((kobj_t)f, M_FEEDER);
132255332Scy}
133255332Scy
134255332Scystatic struct pcm_feeder *
135255332Scyfeeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc)
136145510Sdarrenr{
137255332Scy	struct pcm_feeder *f;
138170268Sdarrenr	int err;
139255332Scy
140255332Scy	f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO);
141255332Scy	if (f == NULL)
142145510Sdarrenr		return NULL;
143145510Sdarrenr
144145510Sdarrenr	f->align = fc->align;
145255332Scy	f->data = fc->data;
146255332Scy	f->source = NULL;
147145510Sdarrenr	f->parent = NULL;
148353093Scy	f->class = fc;
149145510Sdarrenr	f->desc = &(f->desc_static);
150145510Sdarrenr
151145510Sdarrenr	if (desc) {
152145510Sdarrenr		*(f->desc) = *desc;
153145510Sdarrenr	} else {
154145510Sdarrenr		f->desc->type = FEEDER_ROOT;
155145510Sdarrenr		f->desc->in = 0;
156255332Scy		f->desc->out = 0;
157170268Sdarrenr		f->desc->flags = 0;
158145510Sdarrenr		f->desc->idx = 0;
159145510Sdarrenr	}
160145510Sdarrenr
161145510Sdarrenr	err = FEEDER_INIT(f);
162145510Sdarrenr	if (err) {
163255332Scy		printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err);
164145510Sdarrenr		feeder_destroy(f);
165145510Sdarrenr
166255332Scy		return NULL;
167255332Scy	}
168255332Scy
169255332Scy	return f;
170255332Scy}
171145510Sdarrenr
172145510Sdarrenrstruct feeder_class *
173145510Sdarrenrfeeder_getclass(struct pcm_feederdesc *desc)
174145510Sdarrenr{
175255332Scy	struct feedertab_entry *fte;
176255332Scy
177255332Scy	SLIST_FOREACH(fte, &feedertab, link) {
178255332Scy		if ((desc == NULL) && (fte->desc == NULL))
179255332Scy			return fte->feederclass;
180255332Scy		if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc))
181255332Scy			return fte->feederclass;
182255332Scy	}
183255332Scy	return NULL;
184255332Scy}
185255332Scy
186255332Scyint
187255332Scychn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc)
188255332Scy{
189255332Scy	struct pcm_feeder *nf;
190255332Scy
191255332Scy	nf = feeder_create(fc, desc);
192255332Scy	if (nf == NULL)
193255332Scy		return ENOSPC;
194255332Scy
195255332Scy	nf->source = c->feeder;
196255332Scy
197255332Scy	if (nf->align > 0)
198255332Scy		c->align += nf->align;
199255332Scy	else if (nf->align < 0 && c->align < -nf->align)
200255332Scy		c->align = -nf->align;
201145510Sdarrenr
202145510Sdarrenr	c->feeder = nf;
203145510Sdarrenr
204353086Scy	return 0;
205353086Scy}
206353086Scy
207145510Sdarrenrint
208145510Sdarrenrchn_removefeeder(struct pcm_channel *c)
209353086Scy{
210353086Scy	struct pcm_feeder *f;
211353086Scy
212170268Sdarrenr	if (c->feeder == NULL)
213255332Scy		return -1;
214170268Sdarrenr	f = c->feeder;
215170268Sdarrenr	c->feeder = c->feeder->source;
216170268Sdarrenr	feeder_destroy(f);
217145510Sdarrenr
218145510Sdarrenr	return 0;
219145510Sdarrenr}
220170268Sdarrenr
221170268Sdarrenrstruct pcm_feeder *
222145510Sdarrenrchn_findfeeder(struct pcm_channel *c, u_int32_t type)
223170268Sdarrenr{
224170268Sdarrenr	struct pcm_feeder *f;
225145510Sdarrenr
226145510Sdarrenr	f = c->feeder;
227145510Sdarrenr	while (f != NULL) {
228145510Sdarrenr		if (f->desc->type == type)
229145510Sdarrenr			return f;
230255332Scy		f = f->source;
231255332Scy	}
232255332Scy
233255332Scy	return NULL;
234255332Scy}
235255332Scy
236255332Scystatic int
237255332Scychainok(struct pcm_feeder *test, struct pcm_feeder *stop)
238255332Scy{
239255332Scy	u_int32_t visited[MAXFEEDERS / 32];
240255332Scy	u_int32_t idx, mask;
241255332Scy
242255332Scy	bzero(visited, sizeof(visited));
243255332Scy	while (test && (test != stop)) {
244255332Scy		idx = test->desc->idx;
245255332Scy		if (idx < 0)
246145510Sdarrenr			panic("bad idx %d", idx);
247145510Sdarrenr		if (idx >= MAXFEEDERS)
248145510Sdarrenr			panic("bad idx %d", idx);
249145510Sdarrenr		mask = 1 << (idx & 31);
250255332Scy		idx >>= 5;
251255332Scy		if (visited[idx] & mask)
252255332Scy			return 0;
253255332Scy		visited[idx] |= mask;
254145510Sdarrenr		test = test->source;
255145510Sdarrenr	}
256353094Scy
257145510Sdarrenr	return 1;
258145510Sdarrenr}
259145510Sdarrenr
260145510Sdarrenrstatic struct pcm_feeder *
261145510Sdarrenrfeeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth)
262145510Sdarrenr{
263145510Sdarrenr	struct feedertab_entry *fte;
264145510Sdarrenr	struct pcm_feeder *try, *ret;
265145510Sdarrenr
266145510Sdarrenr	/* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */
267145510Sdarrenr	if (fmtvalid(source->desc->out, to)) {
268353094Scy		/* printf("got it\n"); */
269145510Sdarrenr		return source;
270145510Sdarrenr	}
271145510Sdarrenr
272145510Sdarrenr	if (maxdepth < 0)
273145510Sdarrenr		return NULL;
274145510Sdarrenr
275145510Sdarrenr	SLIST_FOREACH(fte, &feedertab, link) {
276145510Sdarrenr		if (fte->desc == NULL)
277145510Sdarrenr			continue;
278145510Sdarrenr		if (fte->desc->type != FEEDER_FMT)
279255332Scy			continue;
280145510Sdarrenr		if (fte->desc->in == source->desc->out) {
281145510Sdarrenr			try = feeder_create(fte->feederclass, fte->desc);
282145510Sdarrenr			if (try) {
283145510Sdarrenr				try->source = source;
284145510Sdarrenr				ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
285145510Sdarrenr				if (ret != NULL)
286145510Sdarrenr					return ret;
287145510Sdarrenr				feeder_destroy(try);
288145510Sdarrenr			}
289353087Scy		}
290353087Scy	}
291353087Scy	/* printf("giving up %s...\n", source->class->name); */
292353087Scy
293145510Sdarrenr	return NULL;
294353094Scy}
295353094Scy
296353094Scyu_int32_t
297353094Scychn_fmtchain(struct pcm_channel *c, u_int32_t *to)
298145510Sdarrenr{
299145510Sdarrenr	struct pcm_feeder *try, *del, *stop;
300145510Sdarrenr	u_int32_t tmpfrom[2], best, *from;
301353084Scy	int i, max, bestmax;
302353084Scy
303353084Scy	KASSERT(c != NULL, ("c == NULL"));
304145510Sdarrenr	KASSERT(c->feeder != NULL, ("c->feeder == NULL"));
305145510Sdarrenr	KASSERT(to != NULL, ("to == NULL"));
306353084Scy	KASSERT(to[0] != 0, ("to[0] == 0"));
307353084Scy
308353084Scy	stop = c->feeder;
309145510Sdarrenr
310145510Sdarrenr	if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) {
311145510Sdarrenr		from = chn_getcaps(c)->fmtlist;
312145510Sdarrenr	} else {
313145510Sdarrenr		tmpfrom[0] = c->feeder->desc->out;
314145510Sdarrenr		tmpfrom[1] = 0;
315145510Sdarrenr		from = tmpfrom;
316145510Sdarrenr	}
317353094Scy
318353094Scy	i = 0;
319353094Scy	best = 0;
320353094Scy	bestmax = 100;
321353094Scy	while (from[i] != 0) {
322353094Scy		c->feeder->desc->out = from[i];
323353094Scy		try = NULL;
324255332Scy		max = 0;
325255332Scy		while (try == NULL && max < 8) {
326255332Scy			try = feeder_fmtchain(to, c->feeder, stop, max);
327353094Scy			if (try == NULL)
328145510Sdarrenr				max++;
329145510Sdarrenr		}
330145510Sdarrenr		if (try != NULL && max < bestmax) {
331353094Scy			bestmax = max;
332353094Scy			best = from[i];
333145510Sdarrenr		}
334145510Sdarrenr		while (try != NULL && try != stop) {
335145510Sdarrenr			del = try;
336145510Sdarrenr			try = try->source;
337145510Sdarrenr			feeder_destroy(del);
338145510Sdarrenr		}
339145510Sdarrenr		i++;
340145510Sdarrenr	}
341145510Sdarrenr	if (best == 0)
342145510Sdarrenr		return 0;
343145510Sdarrenr
344145510Sdarrenr	c->feeder->desc->out = best;
345145510Sdarrenr	try = feeder_fmtchain(to, c->feeder, stop, bestmax);
346145510Sdarrenr	if (try == NULL)
347145510Sdarrenr		return 0;
348145510Sdarrenr
349145510Sdarrenr	c->feeder = try;
350145510Sdarrenr	c->align = 0;
351145510Sdarrenr#ifdef FEEDER_DEBUG
352145510Sdarrenr	printf("\n\nchain: ");
353145510Sdarrenr#endif
354145510Sdarrenr	while (try && (try != stop)) {
355145510Sdarrenr#ifdef FEEDER_DEBUG
356145510Sdarrenr		printf("%s [%d]", try->class->name, try->desc->idx);
357353094Scy		if (try->source)
358353094Scy			printf(" -> ");
359353094Scy#endif
360353094Scy		if (try->source)
361353094Scy			try->source->parent = try;
362353094Scy		if (try->align > 0)
363353094Scy			c->align += try->align;
364353094Scy		else if (try->align < 0 && c->align < -try->align)
365353094Scy			c->align = -try->align;
366353094Scy		try = try->source;
367145510Sdarrenr	}
368145510Sdarrenr#ifdef FEEDER_DEBUG
369145510Sdarrenr	printf("%s [%d]\n", try->class->name, try->desc->idx);
370145510Sdarrenr#endif
371145510Sdarrenr
372145510Sdarrenr	return (c->direction == PCMDIR_REC)? best : c->feeder->desc->out;
373255332Scy}
374255332Scy
375255332Scy/*****************************************************************************/
376255332Scy
377145510Sdarrenrstatic int
378145510Sdarrenrfeed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source)
379145510Sdarrenr{
380358321Scy	struct snd_dbuf *src = source;
381145510Sdarrenr	int l;
382145510Sdarrenr	u_int8_t x;
383145510Sdarrenr
384145510Sdarrenr	KASSERT(count > 0, ("feed_root: count == 0"));
385145510Sdarrenr	/* count &= ~((1 << ch->align) - 1); */
386145510Sdarrenr	KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align));
387358321Scy
388358321Scy	l = min(count, sndbuf_getready(src));
389358321Scy	sndbuf_dispose(src, buffer, l);
390358321Scy
391145510Sdarrenr	/* When recording only return as much data as available */
392255332Scy	if (ch->direction == PCMDIR_REC)
393145510Sdarrenr		return l;
394145510Sdarrenr
395145510Sdarrenr/*
396145510Sdarrenr	if (l < count)
397145510Sdarrenr		printf("appending %d bytes\n", count - l);
398145510Sdarrenr*/
399145510Sdarrenr
400353080Scy	x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80;
401353080Scy	while (l < count)
402353080Scy		buffer[l++] = x;
403145510Sdarrenr
404145510Sdarrenr	return count;
405353080Scy}
406353080Scy
407353080Scystatic kobj_method_t feeder_root_methods[] = {
408145510Sdarrenr    	KOBJMETHOD(feeder_feed,		feed_root),
409145510Sdarrenr	{ 0, 0 }
410145510Sdarrenr};
411255332Scystatic struct feeder_class feeder_root_class = {
412145510Sdarrenr	name:		"feeder_root",
413145510Sdarrenr	methods:	feeder_root_methods,
414145510Sdarrenr	size:		sizeof(struct pcm_feeder),
415145510Sdarrenr	align:		0,
416145510Sdarrenr	desc:		NULL,
417145510Sdarrenr	data:		NULL,
418145510Sdarrenr};
419145510SdarrenrSYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class);
420145510SdarrenrSYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);
421145510Sdarrenr
422145510Sdarrenr
423145510Sdarrenr
424145510Sdarrenr
425255332Scy
426255332Scy