feeder.c revision 65645
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: head/sys/dev/sound/pcm/feeder.c 65645 2000-09-09 21:24:03Z cg $
27 */
28
29#include <dev/sound/pcm/sound.h>
30
31#define FEEDBUFSZ	8192
32#undef FEEDER_DEBUG
33
34static unsigned char ulaw_to_u8[] = {
35     3,    7,   11,   15,   19,   23,   27,   31,
36    35,   39,   43,   47,   51,   55,   59,   63,
37    66,   68,   70,   72,   74,   76,   78,   80,
38    82,   84,   86,   88,   90,   92,   94,   96,
39    98,   99,  100,  101,  102,  103,  104,  105,
40   106,  107,  108,  109,  110,  111,  112,  113,
41   113,  114,  114,  115,  115,  116,  116,  117,
42   117,  118,  118,  119,  119,  120,  120,  121,
43   121,  121,  122,  122,  122,  122,  123,  123,
44   123,  123,  124,  124,  124,  124,  125,  125,
45   125,  125,  125,  125,  126,  126,  126,  126,
46   126,  126,  126,  126,  127,  127,  127,  127,
47   127,  127,  127,  127,  127,  127,  127,  127,
48   128,  128,  128,  128,  128,  128,  128,  128,
49   128,  128,  128,  128,  128,  128,  128,  128,
50   128,  128,  128,  128,  128,  128,  128,  128,
51   253,  249,  245,  241,  237,  233,  229,  225,
52   221,  217,  213,  209,  205,  201,  197,  193,
53   190,  188,  186,  184,  182,  180,  178,  176,
54   174,  172,  170,  168,  166,  164,  162,  160,
55   158,  157,  156,  155,  154,  153,  152,  151,
56   150,  149,  148,  147,  146,  145,  144,  143,
57   143,  142,  142,  141,  141,  140,  140,  139,
58   139,  138,  138,  137,  137,  136,  136,  135,
59   135,  135,  134,  134,  134,  134,  133,  133,
60   133,  133,  132,  132,  132,  132,  131,  131,
61   131,  131,  131,  131,  130,  130,  130,  130,
62   130,  130,  130,  130,  129,  129,  129,  129,
63   129,  129,  129,  129,  129,  129,  129,  129,
64   128,  128,  128,  128,  128,  128,  128,  128,
65   128,  128,  128,  128,  128,  128,  128,  128,
66   128,  128,  128,  128,  128,  128,  128,  128,
67};
68
69static unsigned char u8_to_ulaw[] = {
70     0,    0,    0,    0,    0,    1,    1,    1,
71     1,    2,    2,    2,    2,    3,    3,    3,
72     3,    4,    4,    4,    4,    5,    5,    5,
73     5,    6,    6,    6,    6,    7,    7,    7,
74     7,    8,    8,    8,    8,    9,    9,    9,
75     9,   10,   10,   10,   10,   11,   11,   11,
76    11,   12,   12,   12,   12,   13,   13,   13,
77    13,   14,   14,   14,   14,   15,   15,   15,
78    15,   16,   16,   17,   17,   18,   18,   19,
79    19,   20,   20,   21,   21,   22,   22,   23,
80    23,   24,   24,   25,   25,   26,   26,   27,
81    27,   28,   28,   29,   29,   30,   30,   31,
82    31,   32,   33,   34,   35,   36,   37,   38,
83    39,   40,   41,   42,   43,   44,   45,   46,
84    47,   49,   51,   53,   55,   57,   59,   61,
85    63,   66,   70,   74,   78,   84,   92,  104,
86   254,  231,  219,  211,  205,  201,  197,  193,
87   190,  188,  186,  184,  182,  180,  178,  176,
88   175,  174,  173,  172,  171,  170,  169,  168,
89   167,  166,  165,  164,  163,  162,  161,  160,
90   159,  159,  158,  158,  157,  157,  156,  156,
91   155,  155,  154,  154,  153,  153,  152,  152,
92   151,  151,  150,  150,  149,  149,  148,  148,
93   147,  147,  146,  146,  145,  145,  144,  144,
94   143,  143,  143,  143,  142,  142,  142,  142,
95   141,  141,  141,  141,  140,  140,  140,  140,
96   139,  139,  139,  139,  138,  138,  138,  138,
97   137,  137,  137,  137,  136,  136,  136,  136,
98   135,  135,  135,  135,  134,  134,  134,  134,
99   133,  133,  133,  133,  132,  132,  132,  132,
100   131,  131,  131,  131,  130,  130,  130,  130,
101   129,  129,  129,  129,  128,  128,  128,  128,
102};
103
104struct feedertab_entry {
105	SLIST_ENTRY(feedertab_entry) link;
106	pcm_feeder *feeder;
107	struct pcm_feederdesc *desc;
108
109	int idx;
110};
111static SLIST_HEAD(, feedertab_entry) feedertab;
112
113/*****************************************************************************/
114
115void
116feeder_register(void *p)
117{
118	pcm_feeder *f = p;
119	struct feedertab_entry *fte;
120	static int feedercnt = 0;
121	int i;
122
123	if (feedercnt == 0) {
124		if (f->desc)
125			panic("FIRST FEEDER NOT ROOT: %s\n", f->name);
126		SLIST_INIT(&feedertab);
127		fte = malloc(sizeof(*fte), M_DEVBUF, M_NOWAIT);
128		fte->feeder = f;
129		fte->desc = NULL;
130		fte->idx = feedercnt;
131		SLIST_INSERT_HEAD(&feedertab, fte, link);
132		feedercnt++;
133		return;
134	}
135	/* printf("installing feeder: %s\n", f->name); */
136
137	i = 0;
138	while ((feedercnt < MAXFEEDERS) && (f->desc[i].type > 0)) {
139		fte = malloc(sizeof(*fte), M_DEVBUF, M_NOWAIT);
140		fte->feeder = f;
141		fte->desc = &f->desc[i];
142		fte->idx = feedercnt;
143		fte->desc->idx = feedercnt;
144		SLIST_INSERT_HEAD(&feedertab, fte, link);
145		i++;
146	}
147	feedercnt++;
148	if (feedercnt >= MAXFEEDERS)
149		printf("MAXFEEDERS exceeded\n");
150}
151
152/*****************************************************************************/
153
154static int
155feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream)
156{
157	int ret, s;
158
159	KASSERT(count, ("feed_root: count == 0"));
160	count &= ~((1 << ch->align) - 1);
161	KASSERT(count, ("feed_root: aligned count == 0"));
162
163	s = spltty();
164	count = min(count, stream->uio_resid);
165	if (count) {
166		ret = uiomove(buffer, count, stream);
167		KASSERT(ret == 0, ("feed_root: uiomove failed"));
168	}
169	splx(s);
170
171	return count;
172}
173static pcm_feeder feeder_root = { "root", 0, NULL, NULL, NULL, feed_root };
174SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root);
175
176/*****************************************************************************/
177
178static int
179feed_8to16le(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
180{
181	int i, j, k;
182
183	k = f->source->feed(f->source, c, b, count / 2, stream);
184	j = k - 1;
185	i = j * 2 + 1;
186	while (i > 0 && j >= 0) {
187		b[i--] = b[j--];
188		b[i--] = 0;
189	}
190	return k * 2;
191}
192
193static struct pcm_feederdesc desc_8to16le[] = {
194	{FEEDER_FMT, AFMT_U8, AFMT_U16_LE, 0},
195	{FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
196	{FEEDER_FMT, AFMT_S8, AFMT_S16_LE, 0},
197	{FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
198	{0},
199};
200static pcm_feeder feeder_8to16le =
201	{ "8to16le", 0, desc_8to16le, NULL, NULL, feed_8to16le };
202SYSINIT(feeder_8to16le, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_8to16le);
203
204/*****************************************************************************/
205
206static int
207feed_16to8_init(pcm_feeder *f)
208{
209	f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
210	return (f->data == NULL);
211}
212
213static int
214feed_16to8_free(pcm_feeder *f)
215{
216	if (f->data) free(f->data, M_DEVBUF);
217	f->data = NULL;
218	return 0;
219}
220
221static int
222feed_16leto8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
223{
224	u_int32_t i = 0, toget = count * 2;
225	int j = 1, k;
226
227	k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
228	while (j < k) {
229		b[i++] = ((u_int8_t *)f->data)[j];
230		j += 2;
231	}
232	return i;
233}
234
235static struct pcm_feederdesc desc_16leto8[] = {
236	{FEEDER_FMT, AFMT_U16_LE, AFMT_U8, 0},
237	{FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
238	{FEEDER_FMT, AFMT_S16_LE, AFMT_S8, 0},
239	{FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
240	{0},
241};
242static pcm_feeder feeder_16leto8 =
243	{ "16leto8", 1, desc_16leto8, feed_16to8_init, feed_16to8_free, feed_16leto8 };
244SYSINIT(feeder_16leto8, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_16leto8);
245
246/*****************************************************************************/
247
248static int
249feed_monotostereo8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
250{
251	int i, j, k = f->source->feed(f->source, c, b, count / 2, stream);
252
253	j = k - 1;
254	i = j * 2 + 1;
255	while (i > 0 && j >= 0) {
256		b[i--] = b[j];
257		b[i--] = b[j];
258		j--;
259	}
260	return k * 2;
261}
262
263static struct pcm_feederdesc desc_monotostereo8[] = {
264	{FEEDER_FMT, AFMT_U8, AFMT_U8 | AFMT_STEREO, 0},
265	{FEEDER_FMT, AFMT_S8, AFMT_S8 | AFMT_STEREO, 0},
266	{0},
267};
268static pcm_feeder feeder_monotostereo8 =
269	{ "monotostereo8", 0, desc_monotostereo8, NULL, NULL, feed_monotostereo8 };
270SYSINIT(feeder_monotostereo8, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_monotostereo8);
271
272/*****************************************************************************/
273
274static int
275feed_monotostereo16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
276{
277	int i, j, k = f->source->feed(f->source, c, b, count / 2, stream);
278	u_int8_t x, y;
279
280	j = k - 1;
281	i = j * 2 + 1;
282	while (i > 3 && j >= 1) {
283		x = b[j--];
284		y = b[j--];
285		b[i--] = x;
286		b[i--] = y;
287		b[i--] = x;
288		b[i--] = y;
289	}
290	return k * 2;
291}
292
293static struct pcm_feederdesc desc_monotostereo16[] = {
294	{FEEDER_FMT, AFMT_U16_LE, AFMT_U16_LE | AFMT_STEREO, 0},
295	{FEEDER_FMT, AFMT_S16_LE, AFMT_S16_LE | AFMT_STEREO, 0},
296	{FEEDER_FMT, AFMT_U16_BE, AFMT_U16_BE | AFMT_STEREO, 0},
297	{FEEDER_FMT, AFMT_S16_BE, AFMT_S16_BE | AFMT_STEREO, 0},
298	{0},
299};
300static pcm_feeder feeder_monotostereo16 =
301	{ "monotostereo16", 0, desc_monotostereo16, NULL, NULL, feed_monotostereo16 };
302SYSINIT(feeder_monotostereo16, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_monotostereo16);
303
304/*****************************************************************************/
305
306static int
307feed_stereotomono8_init(pcm_feeder *f)
308{
309	f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
310	return (f->data == NULL);
311}
312
313static int
314feed_stereotomono8_free(pcm_feeder *f)
315{
316	if (f->data) free(f->data, M_DEVBUF);
317	f->data = NULL;
318	return 0;
319}
320
321static int
322feed_stereotomono8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
323{
324	u_int32_t i = 0, toget = count * 2;
325	int j = 0, k;
326
327	k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
328	while (j < k) {
329		b[i++] = ((u_int8_t *)f->data)[j];
330		j += 2;
331	}
332	return i;
333}
334
335static struct pcm_feederdesc desc_stereotomono8[] = {
336	{FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U8, 0},
337	{FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S8, 0},
338	{0},
339};
340static pcm_feeder feeder_stereotomono8 =
341	{ "stereotomono8", 1, desc_stereotomono8, feed_stereotomono8_init, feed_stereotomono8_free, feed_stereotomono8 };
342SYSINIT(feeder_stereotomono8, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_stereotomono8);
343
344/*****************************************************************************/
345
346static int
347feed_stereotomono16_init(pcm_feeder *f)
348{
349	f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
350	return (f->data == NULL);
351}
352
353static int
354feed_stereotomono16_free(pcm_feeder *f)
355{
356	if (f->data) free(f->data, M_DEVBUF);
357	f->data = NULL;
358	return 0;
359}
360
361static int
362feed_stereotomono16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
363{
364	u_int32_t i = 0, toget = count * 2;
365	int j = 0, k;
366
367	k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
368	while (j < k) {
369		b[i++] = ((u_int8_t *)f->data)[j];
370		b[i++] = ((u_int8_t *)f->data)[j + 1];
371		j += 4;
372	}
373	return i;
374}
375
376static struct pcm_feederdesc desc_stereotomono16[] = {
377	{FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE, 0},
378	{FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE, 0},
379	{FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE, 0},
380	{FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE, 0},
381	{0},
382};
383static pcm_feeder feeder_stereotomono16 =
384	{ "stereotomono16", 1, desc_stereotomono16, feed_stereotomono16_init, feed_stereotomono16_free, feed_stereotomono16 };
385SYSINIT(feeder_stereotomono16, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_stereotomono16);
386
387/*****************************************************************************/
388
389static int
390feed_endian(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
391{
392	u_int8_t t;
393	int i = 0, j = f->source->feed(f->source, c, b, count, stream);
394
395	while (i < j) {
396		t = b[i];
397		b[i] = b[i + 1];
398		b[i + 1] = t;
399		i += 2;
400	}
401	return i;
402}
403
404static struct pcm_feederdesc desc_endian[] = {
405	{FEEDER_FMT, AFMT_U16_LE, AFMT_U16_BE, 0},
406	{FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
407	{FEEDER_FMT, AFMT_S16_LE, AFMT_S16_BE, 0},
408	{FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
409	{FEEDER_FMT, AFMT_U16_BE, AFMT_U16_LE, 0},
410	{FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
411	{FEEDER_FMT, AFMT_S16_BE, AFMT_S16_LE, 0},
412	{FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
413	{0},
414};
415static pcm_feeder feeder_endian = { "endian", -1, desc_endian, NULL, NULL, feed_endian };
416SYSINIT(feeder_endian, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_endian);
417
418/*****************************************************************************/
419
420static int
421feed_sign(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
422{
423	int i = 0, j = f->source->feed(f->source, c, b, count, stream);
424	int ssz = (int)f->data, ofs = ssz - 1;
425
426	while (i < j) {
427		b[i + ofs] ^= 0x80;
428		i += ssz;
429	}
430	return i;
431}
432
433static struct pcm_feederdesc desc_sign8[] = {
434	{FEEDER_FMT, AFMT_U8, AFMT_S8, 0},
435	{FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
436	{FEEDER_FMT, AFMT_S8, AFMT_U8, 0},
437	{FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
438	{0},
439};
440static pcm_feeder feeder_sign8 =
441	{ "sign8", 0, desc_sign8, NULL, NULL, feed_sign, (void *)1 };
442SYSINIT(feeder_sign8, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_sign8);
443
444static struct pcm_feederdesc desc_sign16le[] = {
445	{FEEDER_FMT, AFMT_U16_LE, AFMT_S16_LE, 0},
446	{FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
447	{FEEDER_FMT, AFMT_S16_LE, AFMT_U16_LE, 0},
448	{FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
449	{0},
450};
451static pcm_feeder feeder_sign16le =
452	{ "sign16le", -1, desc_sign16le, NULL, NULL, feed_sign, (void *)2 };
453SYSINIT(feeder_sign16le, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_sign16le);
454
455/*****************************************************************************/
456
457static int
458feed_table(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
459{
460	int i = 0, j = f->source->feed(f->source, c, b, count, stream);
461
462	while (i < j) {
463		b[i] = ((u_int8_t *)f->data)[b[i]];
464		i++;
465	}
466	return i;
467}
468
469static struct pcm_feederdesc desc_ulawtou8[] = {
470	{FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0},
471	{FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
472	{0},
473};
474static pcm_feeder feeder_ulawtou8 =
475	{ "ulawtou8", 0, desc_ulawtou8, NULL, NULL, feed_table, ulaw_to_u8 };
476SYSINIT(feeder_ulawtou8, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_ulawtou8);
477
478static struct pcm_feederdesc desc_u8toulaw[] = {
479	{FEEDER_FMT, AFMT_U8, AFMT_MU_LAW, 0},
480	{FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0},
481	{0},
482};
483static pcm_feeder feeder_u8toulaw =
484	{ "u8toulaw", 0, desc_u8toulaw, NULL, NULL, feed_table, u8_to_ulaw };
485SYSINIT(feeder_u8toulaw, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder_u8toulaw);
486
487/*****************************************************************************/
488
489static int
490cmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m)
491{
492	return ((n->type == m->type) && (n->in == m->in) && (n->out == m->out) && (n->flags == m->flags));
493}
494
495pcm_feeder *
496feeder_get(struct pcm_feederdesc *desc)
497{
498	struct feedertab_entry *fte;
499
500	SLIST_FOREACH(fte, &feedertab, link) {
501		if ((fte->desc != NULL) && cmpdesc(desc, fte->desc))
502			return fte->feeder;
503	}
504	return NULL;
505}
506
507pcm_feeder *
508feeder_getroot()
509{
510	struct feedertab_entry *fte;
511
512	SLIST_FOREACH(fte, &feedertab, link) {
513		if (fte->desc == NULL)
514			return fte->feeder;
515	}
516	return NULL;
517}
518
519int
520chn_removefeeder(pcm_channel *c)
521{
522	pcm_feeder *f;
523
524	if (c->feeder->source == NULL)
525		return -1;
526	f = c->feeder->source;
527	if (c->feeder->free)
528		c->feeder->free(c->feeder);
529	free(c->feeder, M_DEVBUF);
530	c->feeder = f;
531	return 0;
532}
533
534static int
535chainok(pcm_feeder *test, pcm_feeder *stop)
536{
537	u_int32_t visited[MAXFEEDERS / 32];
538	u_int32_t idx, mask;
539
540	bzero(visited, sizeof(visited));
541	while (test && (test != stop)) {
542		idx = test->desc->idx;
543		if (idx < 0)
544			panic("bad idx %d", idx);
545		if (idx >= MAXFEEDERS)
546			panic("bad idx %d", idx);
547		mask = 1 << (idx & 31);
548		idx >>= 5;
549		if (visited[idx] & mask)
550			return 0;
551		visited[idx] |= mask;
552		test = test->source;
553	}
554	return 1;
555}
556
557static pcm_feeder *
558feeder_fmtchain(u_int32_t *to, pcm_feeder *source, pcm_feeder *stop, int maxdepth)
559{
560	struct feedertab_entry *fte;
561	pcm_feeder *try, *ret;
562	struct pcm_feederdesc *trydesc;
563
564	/* printf("trying %s...\n", source->name); */
565	if (fmtvalid(source->desc->out, to)) {
566		/* printf("got it\n"); */
567		return source;
568	}
569
570	if (maxdepth < 0)
571		return NULL;
572
573	try = malloc(sizeof(*try), M_DEVBUF, M_NOWAIT);
574	trydesc = malloc(sizeof(*trydesc), M_DEVBUF, M_NOWAIT);
575	trydesc->type = FEEDER_FMT;
576	trydesc->in = source->desc->out;
577	trydesc->out = 0;
578	trydesc->flags = 0;
579	trydesc->idx = -1;
580
581	SLIST_FOREACH(fte, &feedertab, link) {
582		if ((fte->desc) && (fte->desc->in == source->desc->out)) {
583			*try = *(fte->feeder);
584			try->source = source;
585			try->desc = trydesc;
586			trydesc->out = fte->desc->out;
587			trydesc->idx = fte->idx;
588			ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
589			if (ret != NULL)
590				return ret;
591		}
592	}
593	free(try, M_DEVBUF);
594	free(trydesc, M_DEVBUF);
595	/* printf("giving up %s...\n", source->name); */
596	return NULL;
597}
598
599u_int32_t
600chn_feedchain(pcm_channel *c, u_int32_t *to)
601{
602	pcm_feeder *try, *stop;
603	int max;
604
605	stop = c->feeder;
606	try = NULL;
607	max = 0;
608	while (try == NULL && max < 8) {
609		try = feeder_fmtchain(to, c->feeder, stop, max);
610		max++;
611	}
612	if (try == NULL)
613		return 0;
614	c->feeder = try;
615	c->align = 0;
616#ifdef FEEDER_DEBUG
617	printf("chain: ");
618#endif
619	while (try && (try != stop)) {
620#ifdef FEEDER_DEBUG
621		printf("%s [%d]", try->name, try->desc->idx);
622		if (try->source)
623			printf(" -> ");
624#endif
625		if (try->init)
626			try->init(try);
627		if (try->align > 0)
628			c->align += try->align;
629		else if (try->align < 0 && c->align < -try->align)
630			c->align = -try->align;
631		try = try->source;
632	}
633#ifdef FEEDER_DEBUG
634	printf("%s [%d]\n", try->name, try->desc->idx);
635#endif
636	return c->feeder->desc->out;
637}
638