xref: /freebsd/sys/dev/sound/pcm/feeder.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
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 
31 SND_DECLARE_FILE("$FreeBSD$");
32 
33 MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
34 
35 #define MAXFEEDERS 	256
36 #undef FEEDER_DEBUG
37 
38 struct feedertab_entry {
39 	SLIST_ENTRY(feedertab_entry) link;
40 	struct feeder_class *feederclass;
41 	struct pcm_feederdesc *desc;
42 
43 	int idx;
44 };
45 static SLIST_HEAD(, feedertab_entry) feedertab;
46 
47 /*****************************************************************************/
48 
49 void
50 feeder_register(void *p)
51 {
52 	struct feeder_class *fc = p;
53 	struct feedertab_entry *fte;
54 	static int feedercnt = 0;
55 	int i;
56 
57 	if (feedercnt == 0) {
58 		if (fc->desc)
59 			panic("FIRST FEEDER NOT ROOT: %s\n", fc->name);
60 		SLIST_INIT(&feedertab);
61 		fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
62 		fte->feederclass = fc;
63 		fte->desc = NULL;
64 		fte->idx = feedercnt;
65 		SLIST_INSERT_HEAD(&feedertab, fte, link);
66 		feedercnt++;
67 		return;
68 	}
69 
70 	i = 0;
71 	while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) {
72 		/* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */
73 		fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
74 		fte->feederclass = fc;
75 		fte->desc = &fc->desc[i];
76 		fte->idx = feedercnt;
77 		fte->desc->idx = feedercnt;
78 		SLIST_INSERT_HEAD(&feedertab, fte, link);
79 		i++;
80 	}
81 	feedercnt++;
82 	if (feedercnt >= MAXFEEDERS)
83 		printf("MAXFEEDERS exceeded\n");
84 }
85 
86 static void
87 feeder_unregisterall(void *p)
88 {
89 	struct feedertab_entry *fte, *next;
90 
91 	next = SLIST_FIRST(&feedertab);
92 	while (next != NULL) {
93 		fte = next;
94 		next = SLIST_NEXT(fte, link);
95 		free(fte, M_FEEDER);
96 	}
97 }
98 
99 static int
100 cmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m)
101 {
102 	return ((n->type == m->type) &&
103 		((n->in == 0) || (n->in == m->in)) &&
104 		((n->out == 0) || (n->out == m->out)) &&
105 		(n->flags == m->flags));
106 }
107 
108 static void
109 feeder_destroy(struct pcm_feeder *f)
110 {
111 	FEEDER_FREE(f);
112 	free(f->desc, M_FEEDER);
113 	kobj_delete((kobj_t)f, M_FEEDER);
114 }
115 
116 static struct pcm_feeder *
117 feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc)
118 {
119 	struct pcm_feeder *f;
120 	int err;
121 
122 	f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_WAITOK | M_ZERO);
123 	f->align = fc->align;
124 	f->desc = malloc(sizeof(*(f->desc)), M_FEEDER, M_WAITOK | M_ZERO);
125 	if (desc)
126 		*(f->desc) = *desc;
127 	else {
128 		f->desc->type = FEEDER_ROOT;
129 		f->desc->in = 0;
130 		f->desc->out = 0;
131 		f->desc->flags = 0;
132 		f->desc->idx = 0;
133 	}
134 	f->data = fc->data;
135 	f->source = NULL;
136 	f->class = fc;
137 	err = FEEDER_INIT(f);
138 	if (err) {
139 		printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err);
140 		feeder_destroy(f);
141 		return NULL;
142 	} else
143 		return f;
144 }
145 
146 struct feeder_class *
147 feeder_getclass(struct pcm_feederdesc *desc)
148 {
149 	struct feedertab_entry *fte;
150 
151 	SLIST_FOREACH(fte, &feedertab, link) {
152 		if ((desc == NULL) && (fte->desc == NULL))
153 			return fte->feederclass;
154 		if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc))
155 			return fte->feederclass;
156 	}
157 	return NULL;
158 }
159 
160 int
161 chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc)
162 {
163 	struct pcm_feeder *nf;
164 
165 	nf = feeder_create(fc, desc);
166 	if (nf == NULL)
167 		return -1;
168 
169 	nf->source = c->feeder;
170 
171 	if (nf->align > 0)
172 		c->align += nf->align;
173 	else if (nf->align < 0 && c->align < -nf->align)
174 		c->align = -nf->align;
175 
176 	c->feeder = nf;
177 
178 	return 0;
179 }
180 
181 int
182 chn_removefeeder(struct pcm_channel *c)
183 {
184 	struct pcm_feeder *f;
185 
186 	if (c->feeder == NULL)
187 		return -1;
188 	f = c->feeder;
189 	c->feeder = c->feeder->source;
190 	feeder_destroy(f);
191 	return 0;
192 }
193 
194 struct pcm_feeder *
195 chn_findfeeder(struct pcm_channel *c, u_int32_t type)
196 {
197 	struct pcm_feeder *f;
198 
199 	f = c->feeder;
200 	while (f != NULL) {
201 		if (f->desc->type == type)
202 			return f;
203 		f = f->source;
204 	}
205 	return NULL;
206 }
207 
208 static int
209 chainok(struct pcm_feeder *test, struct pcm_feeder *stop)
210 {
211 	u_int32_t visited[MAXFEEDERS / 32];
212 	u_int32_t idx, mask;
213 
214 	bzero(visited, sizeof(visited));
215 	while (test && (test != stop)) {
216 		idx = test->desc->idx;
217 		if (idx < 0)
218 			panic("bad idx %d", idx);
219 		if (idx >= MAXFEEDERS)
220 			panic("bad idx %d", idx);
221 		mask = 1 << (idx & 31);
222 		idx >>= 5;
223 		if (visited[idx] & mask)
224 			return 0;
225 		visited[idx] |= mask;
226 		test = test->source;
227 	}
228 	return 1;
229 }
230 
231 static struct pcm_feeder *
232 feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth)
233 {
234 	struct feedertab_entry *fte;
235 	struct pcm_feeder *try, *ret;
236 
237 	/* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */
238 	if (fmtvalid(source->desc->out, to)) {
239 		/* printf("got it\n"); */
240 		return source;
241 	}
242 
243 	if (maxdepth < 0)
244 		return NULL;
245 
246 	SLIST_FOREACH(fte, &feedertab, link) {
247 		if (fte->desc == NULL)
248 			goto no;
249 		if (fte->desc->type != FEEDER_FMT)
250 			goto no;
251 		if (fte->desc->in == source->desc->out) {
252 			try = feeder_create(fte->feederclass, fte->desc);
253 			if (try) {
254 				try->source = source;
255 				ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
256 				if (ret != NULL)
257 					return ret;
258 				feeder_destroy(try);
259 			}
260 		}
261 no:
262 	}
263 	/* printf("giving up %s...\n", source->class->name); */
264 	return NULL;
265 }
266 
267 u_int32_t
268 chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
269 {
270 	struct pcm_feeder *try, *stop;
271 	int max;
272 
273 	/* we're broken for recording currently, reject attempts */
274 	if (c->direction == PCMDIR_REC)
275 		return EINVAL;
276 	stop = c->feeder;
277 	try = NULL;
278 	max = 0;
279 	while (try == NULL && max < 8) {
280 		try = feeder_fmtchain(to, c->feeder, stop, max);
281 		max++;
282 	}
283 	if (try == NULL)
284 		return 0;
285 	c->feeder = try;
286 	c->align = 0;
287 #ifdef FEEDER_DEBUG
288 	printf("chain: ");
289 #endif
290 	while (try && (try != stop)) {
291 #ifdef FEEDER_DEBUG
292 		printf("%s [%d]", try->class->name, try->desc->idx);
293 		if (try->source)
294 			printf(" -> ");
295 #endif
296 		if (try->align > 0)
297 			c->align += try->align;
298 		else if (try->align < 0 && c->align < -try->align)
299 			c->align = -try->align;
300 		try = try->source;
301 	}
302 #ifdef FEEDER_DEBUG
303 	printf("%s [%d]\n", try->class->name, try->desc->idx);
304 #endif
305 	return c->feeder->desc->out;
306 }
307 
308 /*****************************************************************************/
309 
310 static int
311 feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source)
312 {
313 	struct snd_dbuf *src = source;
314 	int l;
315 	u_int8_t x;
316 
317 	KASSERT(count > 0, ("feed_root: count == 0"));
318 	/* count &= ~((1 << ch->align) - 1); */
319 	KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align));
320 
321 	l = min(count, sndbuf_getready(src));
322 	sndbuf_dispose(src, buffer, l);
323 
324 /*
325 	if (l < count)
326 		printf("appending %d bytes\n", count - l);
327 */
328 
329 	x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80;
330 	while (l < count)
331 		buffer[l++] = x;
332 
333 	return count;
334 }
335 
336 static kobj_method_t feeder_root_methods[] = {
337     	KOBJMETHOD(feeder_feed,		feed_root),
338 	{ 0, 0 }
339 };
340 static struct feeder_class feeder_root_class = {
341 	name:		"feeder_root",
342 	methods:	feeder_root_methods,
343 	size:		sizeof(struct pcm_feeder),
344 	align:		0,
345 	desc:		NULL,
346 	data:		NULL,
347 };
348 SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class);
349 SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);
350 
351 
352 
353 
354 
355