xref: /freebsd/sys/dev/sound/pcm/feeder.c (revision c824383b269d8abe175ea4751194660716d5600e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
5  * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
6  * All rights reserved.
7  * Copyright (c) 2024-2025 The FreeBSD Foundation
8  *
9  * Portions of this software were developed by Christos Margiolis
10  * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifdef HAVE_KERNEL_OPTION_HEADERS
35 #include "opt_snd.h"
36 #endif
37 
38 #include <dev/sound/pcm/sound.h>
39 #include <dev/sound/pcm/vchan.h>
40 
41 #include "feeder_if.h"
42 
43 static MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
44 
45 #define MAXFEEDERS 	256
46 
47 struct feedertab_entry {
48 	SLIST_ENTRY(feedertab_entry) link;
49 	struct feeder_class *feederclass;
50 	struct pcm_feederdesc *desc;
51 
52 	int idx;
53 };
54 static SLIST_HEAD(, feedertab_entry) feedertab;
55 static int feedercnt = 0;
56 
57 /*****************************************************************************/
58 
59 static void
feeder_register_root(void * p)60 feeder_register_root(void *p)
61 {
62 	struct feeder_class *fc = p;
63 	struct feedertab_entry *fte;
64 
65 	MPASS(feedercnt == 0);
66 	KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name));
67 
68 	SLIST_INIT(&feedertab);
69 	fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
70 	fte->feederclass = fc;
71 	fte->desc = NULL;
72 	fte->idx = feedercnt;
73 	SLIST_INSERT_HEAD(&feedertab, fte, link);
74 	feedercnt++;
75 }
76 
77 void
feeder_register(void * p)78 feeder_register(void *p)
79 {
80 	struct feeder_class *fc = p;
81 	struct feedertab_entry *fte;
82 	int i;
83 
84 	KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name));
85 
86 	/*
87 	 * beyond this point failure is non-fatal but may result in some
88 	 * translations being unavailable
89 	 */
90 	i = 0;
91 	while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) {
92 		fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
93 		fte->feederclass = fc;
94 		fte->desc = &fc->desc[i];
95 		fte->idx = feedercnt;
96 		fte->desc->idx = feedercnt;
97 		SLIST_INSERT_HEAD(&feedertab, fte, link);
98 		i++;
99 	}
100 	feedercnt++;
101 	if (feedercnt >= MAXFEEDERS) {
102 		printf("MAXFEEDERS (%d >= %d) exceeded\n",
103 		    feedercnt, MAXFEEDERS);
104 	}
105 }
106 
107 static void
feeder_unregisterall(void * p)108 feeder_unregisterall(void *p)
109 {
110 	struct feedertab_entry *fte, *next;
111 
112 	next = SLIST_FIRST(&feedertab);
113 	while (next != NULL) {
114 		fte = next;
115 		next = SLIST_NEXT(fte, link);
116 		free(fte, M_FEEDER);
117 	}
118 }
119 
120 static int
cmpdesc(struct pcm_feederdesc * n,struct pcm_feederdesc * m)121 cmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m)
122 {
123 	return ((n->type == m->type) &&
124 		((n->in == 0) || (n->in == m->in)) &&
125 		((n->out == 0) || (n->out == m->out)) &&
126 		(n->flags == m->flags));
127 }
128 
129 static void
feeder_destroy(struct pcm_feeder * f)130 feeder_destroy(struct pcm_feeder *f)
131 {
132 	FEEDER_FREE(f);
133 	kobj_delete((kobj_t)f, M_FEEDER);
134 }
135 
136 static struct pcm_feeder *
feeder_create(struct feeder_class * fc,struct pcm_feederdesc * desc)137 feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc)
138 {
139 	struct pcm_feeder *f;
140 	int err;
141 
142 	f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO);
143 	if (f == NULL)
144 		return NULL;
145 
146 	f->data = fc->data;
147 	f->source = NULL;
148 	f->parent = NULL;
149 	f->class = fc;
150 	f->desc = &(f->desc_static);
151 
152 	if (desc) {
153 		*(f->desc) = *desc;
154 	} else {
155 		f->desc->type = FEEDER_ROOT;
156 		f->desc->in = 0;
157 		f->desc->out = 0;
158 		f->desc->flags = 0;
159 		f->desc->idx = 0;
160 	}
161 
162 	err = FEEDER_INIT(f);
163 	if (err) {
164 		printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err);
165 		feeder_destroy(f);
166 
167 		return NULL;
168 	}
169 
170 	return f;
171 }
172 
173 struct feeder_class *
feeder_getclass(struct pcm_feederdesc * desc)174 feeder_getclass(struct pcm_feederdesc *desc)
175 {
176 	struct feedertab_entry *fte;
177 
178 	SLIST_FOREACH(fte, &feedertab, link) {
179 		if ((desc == NULL) && (fte->desc == NULL))
180 			return fte->feederclass;
181 		if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc))
182 			return fte->feederclass;
183 	}
184 	return NULL;
185 }
186 
187 int
feeder_add(struct pcm_channel * c,struct feeder_class * fc,struct pcm_feederdesc * desc)188 feeder_add(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc)
189 {
190 	struct pcm_feeder *nf;
191 
192 	nf = feeder_create(fc, desc);
193 	if (nf == NULL)
194 		return ENOSPC;
195 
196 	nf->source = c->feeder;
197 
198 	if (c->feeder != NULL)
199 		c->feeder->parent = nf;
200 	c->feeder = nf;
201 
202 	return 0;
203 }
204 
205 void
feeder_remove(struct pcm_channel * c)206 feeder_remove(struct pcm_channel *c)
207 {
208 	struct pcm_feeder *f;
209 
210 	while (c->feeder != NULL) {
211 		f = c->feeder;
212 		c->feeder = c->feeder->source;
213 		feeder_destroy(f);
214 	}
215 }
216 
217 struct pcm_feeder *
feeder_find(struct pcm_channel * c,u_int32_t type)218 feeder_find(struct pcm_channel *c, u_int32_t type)
219 {
220 	struct pcm_feeder *f;
221 
222 	f = c->feeder;
223 	while (f != NULL) {
224 		if (f->desc->type == type)
225 			return f;
226 		f = f->source;
227 	}
228 
229 	return NULL;
230 }
231 
232 /*
233  * 14bit format scoring
234  * --------------------
235  *
236  *  13  12  11  10   9   8        2        1   0    offset
237  * +---+---+---+---+---+---+-------------+---+---+
238  * | X | X | X | X | X | X | X X X X X X | X | X |
239  * +---+---+---+---+---+---+-------------+---+---+
240  *   |   |   |   |   |   |        |        |   |
241  *   |   |   |   |   |   |        |        |   +--> signed?
242  *   |   |   |   |   |   |        |        |
243  *   |   |   |   |   |   |        |        +------> bigendian?
244  *   |   |   |   |   |   |        |
245  *   |   |   |   |   |   |        +---------------> total channels
246  *   |   |   |   |   |   |
247  *   |   |   |   |   |   +------------------------> AFMT_A_LAW
248  *   |   |   |   |   |
249  *   |   |   |   |   +----------------------------> AFMT_MU_LAW
250  *   |   |   |   |
251  *   |   |   |   +--------------------------------> AFMT_8BIT
252  *   |   |   |
253  *   |   |   +------------------------------------> AFMT_16BIT
254  *   |   |
255  *   |   +----------------------------------------> AFMT_24BIT
256  *   |
257  *   +--------------------------------------------> AFMT_32BIT
258  */
259 #define score_signeq(s1, s2)	(((s1) & 0x1) == ((s2) & 0x1))
260 #define score_endianeq(s1, s2)	(((s1) & 0x2) == ((s2) & 0x2))
261 #define score_cheq(s1, s2)	(((s1) & 0xfc) == ((s2) & 0xfc))
262 #define score_chgt(s1, s2)	(((s1) & 0xfc) > ((s2) & 0xfc))
263 #define score_chlt(s1, s2)	(((s1) & 0xfc) < ((s2) & 0xfc))
264 #define score_val(s1)		((s1) & 0x3f00)
265 #define score_cse(s1)		((s1) & 0x7f)
266 
267 u_int32_t
snd_fmtscore(u_int32_t fmt)268 snd_fmtscore(u_int32_t fmt)
269 {
270 	u_int32_t ret;
271 
272 	ret = 0;
273 	if (fmt & AFMT_SIGNED)
274 		ret |= 1 << 0;
275 	if (fmt & AFMT_BIGENDIAN)
276 		ret |= 1 << 1;
277 	/*if (fmt & AFMT_STEREO)
278 		ret |= (2 & 0x3f) << 2;
279 	else
280 		ret |= (1 & 0x3f) << 2;*/
281 	ret |= (AFMT_CHANNEL(fmt) & 0x3f) << 2;
282 	if (fmt & AFMT_A_LAW)
283 		ret |= 1 << 8;
284 	else if (fmt & AFMT_MU_LAW)
285 		ret |= 1 << 9;
286 	else if (fmt & AFMT_8BIT)
287 		ret |= 1 << 10;
288 	else if (fmt & AFMT_16BIT)
289 		ret |= 1 << 11;
290 	else if (fmt & AFMT_24BIT)
291 		ret |= 1 << 12;
292 	else if (fmt & AFMT_32BIT)
293 		ret |= 1 << 13;
294 
295 	return ret;
296 }
297 
298 static u_int32_t
snd_fmtbestfunc(u_int32_t fmt,u_int32_t * fmts,int cheq)299 snd_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq)
300 {
301 	u_int32_t best, score, score2, oldscore;
302 	int i;
303 
304 	if (fmt == 0 || fmts == NULL || fmts[0] == 0)
305 		return 0;
306 
307 	if (snd_fmtvalid(fmt, fmts))
308 		return fmt;
309 
310 	best = 0;
311 	score = snd_fmtscore(fmt);
312 	oldscore = 0;
313 	for (i = 0; fmts[i] != 0; i++) {
314 		score2 = snd_fmtscore(fmts[i]);
315 		if (cheq && !score_cheq(score, score2) &&
316 		    (score_chlt(score2, score) ||
317 		    (oldscore != 0 && score_chgt(score2, oldscore))))
318 				continue;
319 		if (oldscore == 0 ||
320 			    (score_val(score2) == score_val(score)) ||
321 			    (score_val(score2) == score_val(oldscore)) ||
322 			    (score_val(score2) > score_val(oldscore) &&
323 			    score_val(score2) < score_val(score)) ||
324 			    (score_val(score2) < score_val(oldscore) &&
325 			    score_val(score2) > score_val(score)) ||
326 			    (score_val(oldscore) < score_val(score) &&
327 			    score_val(score2) > score_val(oldscore))) {
328 			if (score_val(oldscore) != score_val(score2) ||
329 				    score_cse(score) == score_cse(score2) ||
330 				    ((score_cse(oldscore) != score_cse(score) &&
331 				    !score_endianeq(score, oldscore) &&
332 				    (score_endianeq(score, score2) ||
333 				    (!score_signeq(score, oldscore) &&
334 				    score_signeq(score, score2)))))) {
335 				best = fmts[i];
336 				oldscore = score2;
337 			}
338 		}
339 	}
340 	return best;
341 }
342 
343 u_int32_t
snd_fmtbestbit(u_int32_t fmt,u_int32_t * fmts)344 snd_fmtbestbit(u_int32_t fmt, u_int32_t *fmts)
345 {
346 	return snd_fmtbestfunc(fmt, fmts, 0);
347 }
348 
349 u_int32_t
snd_fmtbestchannel(u_int32_t fmt,u_int32_t * fmts)350 snd_fmtbestchannel(u_int32_t fmt, u_int32_t *fmts)
351 {
352 	return snd_fmtbestfunc(fmt, fmts, 1);
353 }
354 
355 u_int32_t
snd_fmtbest(u_int32_t fmt,u_int32_t * fmts)356 snd_fmtbest(u_int32_t fmt, u_int32_t *fmts)
357 {
358 	u_int32_t best1, best2;
359 	u_int32_t score, score1, score2;
360 
361 	if (snd_fmtvalid(fmt, fmts))
362 		return fmt;
363 
364 	best1 = snd_fmtbestchannel(fmt, fmts);
365 	best2 = snd_fmtbestbit(fmt, fmts);
366 
367 	if (best1 != 0 && best2 != 0 && best1 != best2) {
368 		/*if (fmt & AFMT_STEREO)*/
369 		if (AFMT_CHANNEL(fmt) > 1)
370 			return best1;
371 		else {
372 			score = score_val(snd_fmtscore(fmt));
373 			score1 = score_val(snd_fmtscore(best1));
374 			score2 = score_val(snd_fmtscore(best2));
375 			if (score1 == score2 || score1 == score)
376 				return best1;
377 			else if (score2 == score)
378 				return best2;
379 			else if (score1 > score2)
380 				return best1;
381 			return best2;
382 		}
383 	} else if (best2 == 0)
384 		return best1;
385 	else
386 		return best2;
387 }
388 
389 void
feeder_printchain(struct pcm_feeder * head)390 feeder_printchain(struct pcm_feeder *head)
391 {
392 	struct pcm_feeder *f;
393 
394 	printf("feeder chain (head @%p)\n", head);
395 	f = head;
396 	while (f != NULL) {
397 		printf("%s/%d @ %p\n", f->class->name, f->desc->idx, f);
398 		f = f->source;
399 	}
400 	printf("[end]\n\n");
401 }
402 
403 /*****************************************************************************/
404 
405 static int
feed_root(struct pcm_feeder * feeder,struct pcm_channel * ch,u_int8_t * buffer,u_int32_t count,void * source)406 feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source)
407 {
408 	struct snd_dbuf *src = source;
409 	int l, offset;
410 
411 	KASSERT(count > 0, ("feed_root: count == 0"));
412 
413 	if (++ch->feedcount == 0)
414 		ch->feedcount = 2;
415 
416 	l = min(count, sndbuf_getready(src));
417 
418 	/* When recording only return as much data as available */
419 	if (ch->direction == PCMDIR_REC) {
420 		sndbuf_dispose(src, buffer, l);
421 		return l;
422 	}
423 
424 	offset = count - l;
425 
426 	if (offset > 0) {
427 		if (snd_verbose > 3)
428 			printf("%s: (%s) %spending %d bytes "
429 			    "(count=%d l=%d feed=%d)\n",
430 			    __func__,
431 			    (ch->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware",
432 			    (ch->feedcount == 1) ? "pre" : "ap",
433 			    offset, count, l, ch->feedcount);
434 
435 		if (ch->feedcount == 1) {
436 			memset(buffer,
437 			    sndbuf_zerodata(sndbuf_getfmt(src)),
438 			    offset);
439 			if (l > 0)
440 				sndbuf_dispose(src, buffer + offset, l);
441 			else
442 				ch->feedcount--;
443 		} else {
444 			if (l > 0)
445 				sndbuf_dispose(src, buffer, l);
446 			memset(buffer + l,
447 			    sndbuf_zerodata(sndbuf_getfmt(src)),
448 			    offset);
449 			if (!(ch->flags & CHN_F_CLOSING))
450 				ch->xruns++;
451 		}
452 	} else if (l > 0)
453 		sndbuf_dispose(src, buffer, l);
454 
455 	return count;
456 }
457 
458 static kobj_method_t feeder_root_methods[] = {
459     	KOBJMETHOD(feeder_feed,		feed_root),
460 	KOBJMETHOD_END
461 };
462 static struct feeder_class feeder_root_class = {
463 	.name =		"feeder_root",
464 	.methods =	feeder_root_methods,
465 	.size =		sizeof(struct pcm_feeder),
466 	.desc =		NULL,
467 	.data =		NULL,
468 };
469 /*
470  * Register the root feeder first so that pcm_addchan() and subsequent
471  * functions can use it.
472  */
473 SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register_root,
474     &feeder_root_class);
475 SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);
476