xref: /freebsd/sys/dev/sound/pcm/vchan.c (revision d6d4586b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2006-2009 Ariff Abdullah <ariff@FreeBSD.org>
5  * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
6  * All rights reserved.
7  * Copyright (c) 2024 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 /* Almost entirely rewritten to add multi-format/channels mixing support. */
35 
36 #ifdef HAVE_KERNEL_OPTION_HEADERS
37 #include "opt_snd.h"
38 #endif
39 
40 #include <dev/sound/pcm/sound.h>
41 #include <dev/sound/pcm/vchan.h>
42 
43 /*
44  * [ac3 , dts , linear , 0, linear, 0]
45  */
46 #define FMTLIST_MAX		6
47 #define FMTLIST_OFFSET		4
48 #define DIGFMTS_MAX		2
49 
50 #ifdef SND_DEBUG
51 static int snd_passthrough_verbose = 0;
52 SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN,
53 	&snd_passthrough_verbose, 0, "passthrough verbosity");
54 
55 #endif
56 
57 struct vchan_info {
58 	struct pcm_channel *channel;
59 	struct pcmchan_caps caps;
60 	uint32_t fmtlist[FMTLIST_MAX];
61 	int trigger;
62 };
63 
64 int snd_maxautovchans = 16;
65 
66 static void *
vchan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)67 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
68     struct pcm_channel *c, int dir)
69 {
70 	struct vchan_info *info;
71 	struct pcm_channel *p;
72 	uint32_t i, j, *fmtlist;
73 
74 	KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC,
75 	    ("vchan_init: bad direction"));
76 	KASSERT(c != NULL && c->parentchannel != NULL,
77 	    ("vchan_init: bad channels"));
78 
79 	info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO);
80 	info->channel = c;
81 	info->trigger = PCMTRIG_STOP;
82 	p = c->parentchannel;
83 
84 	CHN_LOCK(p);
85 
86 	fmtlist = chn_getcaps(p)->fmtlist;
87 	for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) {
88 		if (fmtlist[i] & AFMT_PASSTHROUGH)
89 			info->fmtlist[j++] = fmtlist[i];
90 	}
91 	if (p->format & AFMT_VCHAN)
92 		info->fmtlist[j] = p->format;
93 	else
94 		info->fmtlist[j] = VCHAN_DEFAULT_FORMAT;
95 	info->caps.fmtlist = info->fmtlist +
96 	    ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET);
97 
98 	CHN_UNLOCK(p);
99 
100 	c->flags |= CHN_F_VIRTUAL;
101 
102 	return (info);
103 }
104 
105 static int
vchan_free(kobj_t obj,void * data)106 vchan_free(kobj_t obj, void *data)
107 {
108 
109 	free(data, M_DEVBUF);
110 
111 	return (0);
112 }
113 
114 static int
vchan_setformat(kobj_t obj,void * data,uint32_t format)115 vchan_setformat(kobj_t obj, void *data, uint32_t format)
116 {
117 	struct vchan_info *info;
118 
119 	info = data;
120 
121 	CHN_LOCKASSERT(info->channel);
122 
123 	if (!snd_fmtvalid(format, info->caps.fmtlist))
124 		return (-1);
125 
126 	return (0);
127 }
128 
129 static uint32_t
vchan_setspeed(kobj_t obj,void * data,uint32_t speed)130 vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
131 {
132 	struct vchan_info *info;
133 
134 	info = data;
135 
136 	CHN_LOCKASSERT(info->channel);
137 
138 	return (info->caps.maxspeed);
139 }
140 
141 static int
vchan_trigger(kobj_t obj,void * data,int go)142 vchan_trigger(kobj_t obj, void *data, int go)
143 {
144 	struct vchan_info *info;
145 	struct pcm_channel *c, *p;
146 	int ret, otrigger;
147 
148 	info = data;
149 
150 	if (!PCMTRIG_COMMON(go) || go == info->trigger)
151 		return (0);
152 
153 	c = info->channel;
154 	p = c->parentchannel;
155 	otrigger = info->trigger;
156 	info->trigger = go;
157 
158 	CHN_LOCKASSERT(c);
159 
160 	CHN_UNLOCK(c);
161 	CHN_LOCK(p);
162 
163 	switch (go) {
164 	case PCMTRIG_START:
165 		if (otrigger != PCMTRIG_START)
166 			CHN_INSERT_HEAD(p, c, children.busy);
167 		break;
168 	case PCMTRIG_STOP:
169 	case PCMTRIG_ABORT:
170 		if (otrigger == PCMTRIG_START)
171 			CHN_REMOVE(p, c, children.busy);
172 		break;
173 	default:
174 		break;
175 	}
176 
177 	ret = chn_notify(p, CHN_N_TRIGGER);
178 
179 	CHN_LOCK(c);
180 
181 	if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c))
182 		ret = vchan_sync(c);
183 
184 	CHN_UNLOCK(c);
185 	CHN_UNLOCK(p);
186 	CHN_LOCK(c);
187 
188 	return (ret);
189 }
190 
191 static struct pcmchan_caps *
vchan_getcaps(kobj_t obj,void * data)192 vchan_getcaps(kobj_t obj, void *data)
193 {
194 	struct vchan_info *info;
195 	struct pcm_channel *c;
196 	uint32_t pformat, pspeed, pflags, i;
197 
198 	info = data;
199 	c = info->channel;
200 	pformat = c->parentchannel->format;
201 	pspeed = c->parentchannel->speed;
202 	pflags = c->parentchannel->flags;
203 
204 	CHN_LOCKASSERT(c);
205 
206 	if (pflags & CHN_F_VCHAN_DYNAMIC) {
207 		info->caps.fmtlist = info->fmtlist;
208 		if (pformat & AFMT_VCHAN) {
209 			for (i = 0; info->caps.fmtlist[i] != 0; i++) {
210 				if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH)
211 					continue;
212 				break;
213 			}
214 			info->caps.fmtlist[i] = pformat;
215 		}
216 		if (c->format & AFMT_PASSTHROUGH)
217 			info->caps.minspeed = c->speed;
218 		else
219 			info->caps.minspeed = pspeed;
220 		info->caps.maxspeed = info->caps.minspeed;
221 	} else {
222 		info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET;
223 		if (pformat & AFMT_VCHAN)
224 			info->caps.fmtlist[0] = pformat;
225 		else {
226 			device_printf(c->dev,
227 			    "%s(): invalid vchan format 0x%08x",
228 			    __func__, pformat);
229 			info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT;
230 		}
231 		info->caps.minspeed = pspeed;
232 		info->caps.maxspeed = info->caps.minspeed;
233 	}
234 
235 	return (&info->caps);
236 }
237 
238 static struct pcmchan_matrix *
vchan_getmatrix(kobj_t obj,void * data,uint32_t format)239 vchan_getmatrix(kobj_t obj, void *data, uint32_t format)
240 {
241 
242 	return (feeder_matrix_format_map(format));
243 }
244 
245 static kobj_method_t vchan_methods[] = {
246 	KOBJMETHOD(channel_init,		vchan_init),
247 	KOBJMETHOD(channel_free,		vchan_free),
248 	KOBJMETHOD(channel_setformat,		vchan_setformat),
249 	KOBJMETHOD(channel_setspeed,		vchan_setspeed),
250 	KOBJMETHOD(channel_trigger,		vchan_trigger),
251 	KOBJMETHOD(channel_getcaps,		vchan_getcaps),
252 	KOBJMETHOD(channel_getmatrix,		vchan_getmatrix),
253 	KOBJMETHOD_END
254 };
255 CHANNEL_DECLARE(vchan);
256 
257 static void
vchan_getparentchannel(struct snddev_info * d,struct pcm_channel ** wrch,struct pcm_channel ** rdch)258 vchan_getparentchannel(struct snddev_info *d,
259     struct pcm_channel **wrch, struct pcm_channel **rdch)
260 {
261 	struct pcm_channel **ch, *wch, *rch, *c;
262 
263 	KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__));
264 
265 	PCM_BUSYASSERT(d);
266 	PCM_UNLOCKASSERT(d);
267 
268 	wch = NULL;
269 	rch = NULL;
270 
271 	CHN_FOREACH(c, d, channels.pcm) {
272 		CHN_LOCK(c);
273 		ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch;
274 		if (c->flags & CHN_F_VIRTUAL) {
275 			/* Sanity check */
276 			if (*ch != NULL && *ch != c->parentchannel) {
277 				CHN_UNLOCK(c);
278 				*ch = NULL;
279 				break;
280 			}
281 		} else if (c->flags & CHN_F_HAS_VCHAN) {
282 			/* No way!! */
283 			if (*ch != NULL) {
284 				CHN_UNLOCK(c);
285 				*ch = NULL;
286 				break;
287 			}
288 			*ch = c;
289 		}
290 		CHN_UNLOCK(c);
291 	}
292 
293 	if (wrch != NULL)
294 		*wrch = wch;
295 	if (rdch != NULL)
296 		*rdch = rch;
297 }
298 
299 static int
sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)300 sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
301 {
302 	struct snddev_info *d;
303 	int direction, vchancount;
304 	int err, cnt;
305 
306 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
307 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
308 		return (EINVAL);
309 
310 	PCM_LOCK(d);
311 	PCM_WAIT(d);
312 
313 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
314 	case VCHAN_PLAY:
315 		direction = PCMDIR_PLAY;
316 		vchancount = d->pvchancount;
317 		cnt = d->playcount;
318 		break;
319 	case VCHAN_REC:
320 		direction = PCMDIR_REC;
321 		vchancount = d->rvchancount;
322 		cnt = d->reccount;
323 		break;
324 	default:
325 		PCM_UNLOCK(d);
326 		return (EINVAL);
327 		break;
328 	}
329 
330 	if (cnt < 1) {
331 		PCM_UNLOCK(d);
332 		return (ENODEV);
333 	}
334 
335 	PCM_ACQUIRE(d);
336 	PCM_UNLOCK(d);
337 
338 	cnt = vchancount;
339 	err = sysctl_handle_int(oidp, &cnt, 0, req);
340 
341 	if (err == 0 && req->newptr != NULL && vchancount != cnt) {
342 		if (cnt < 0)
343 			cnt = 0;
344 		if (cnt > SND_MAXVCHANS)
345 			cnt = SND_MAXVCHANS;
346 		err = vchan_setnew(d, direction, cnt);
347 	}
348 
349 	PCM_RELEASE_QUICK(d);
350 
351 	return err;
352 }
353 
354 static int
sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)355 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
356 {
357 	struct snddev_info *d;
358 	struct pcm_channel *c;
359 	uint32_t dflags;
360 	int direction, ret;
361 	char dtype[16];
362 
363 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
364 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
365 		return (EINVAL);
366 
367 	PCM_LOCK(d);
368 	PCM_WAIT(d);
369 
370 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
371 	case VCHAN_PLAY:
372 		direction = PCMDIR_PLAY;
373 		break;
374 	case VCHAN_REC:
375 		direction = PCMDIR_REC;
376 		break;
377 	default:
378 		PCM_UNLOCK(d);
379 		return (EINVAL);
380 		break;
381 	}
382 
383 	PCM_ACQUIRE(d);
384 	PCM_UNLOCK(d);
385 
386 	if (direction == PCMDIR_PLAY)
387 		vchan_getparentchannel(d, &c, NULL);
388 	else
389 		vchan_getparentchannel(d, NULL, &c);
390 
391 	if (c == NULL) {
392 		PCM_RELEASE_QUICK(d);
393 		return (EINVAL);
394 	}
395 
396 	KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
397 	    __func__, direction, c->direction));
398 
399 	CHN_LOCK(c);
400 	if (c->flags & CHN_F_VCHAN_PASSTHROUGH)
401 		strlcpy(dtype, "passthrough", sizeof(dtype));
402 	else if (c->flags & CHN_F_VCHAN_ADAPTIVE)
403 		strlcpy(dtype, "adaptive", sizeof(dtype));
404 	else
405 		strlcpy(dtype, "fixed", sizeof(dtype));
406 	CHN_UNLOCK(c);
407 
408 	ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req);
409 	if (ret == 0 && req->newptr != NULL) {
410 		if (strcasecmp(dtype, "passthrough") == 0 ||
411 		    strcmp(dtype, "1") == 0)
412 			dflags = CHN_F_VCHAN_PASSTHROUGH;
413 		else if (strcasecmp(dtype, "adaptive") == 0 ||
414 		    strcmp(dtype, "2") == 0)
415 			dflags = CHN_F_VCHAN_ADAPTIVE;
416 		else if (strcasecmp(dtype, "fixed") == 0 ||
417 		    strcmp(dtype, "0") == 0)
418 			dflags = 0;
419 		else {
420 			PCM_RELEASE_QUICK(d);
421 			return (EINVAL);
422 		}
423 		CHN_LOCK(c);
424 		if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) ||
425 		    (c->flags & CHN_F_PASSTHROUGH)) {
426 			CHN_UNLOCK(c);
427 			PCM_RELEASE_QUICK(d);
428 			return (0);
429 		}
430 		c->flags &= ~CHN_F_VCHAN_DYNAMIC;
431 		c->flags |= dflags;
432 		CHN_UNLOCK(c);
433 	}
434 
435 	PCM_RELEASE_QUICK(d);
436 
437 	return (ret);
438 }
439 
440 /*
441  * On the fly vchan rate/format settings
442  */
443 
444 #define VCHAN_ACCESSIBLE(c)	(!((c)->flags & (CHN_F_PASSTHROUGH |	\
445 				 CHN_F_EXCLUSIVE)) &&			\
446 				 (((c)->flags & CHN_F_VCHAN_DYNAMIC) ||	\
447 				 CHN_STOPPED(c)))
448 static int
sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)449 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
450 {
451 	struct snddev_info *d;
452 	struct pcm_channel *c, *ch;
453 	struct pcmchan_caps *caps;
454 	int *vchanrate, vchancount, direction, ret, newspd, restart;
455 
456 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
457 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
458 		return (EINVAL);
459 
460 	PCM_LOCK(d);
461 	PCM_WAIT(d);
462 
463 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
464 	case VCHAN_PLAY:
465 		direction = PCMDIR_PLAY;
466 		vchancount = d->pvchancount;
467 		vchanrate = &d->pvchanrate;
468 		break;
469 	case VCHAN_REC:
470 		direction = PCMDIR_REC;
471 		vchancount = d->rvchancount;
472 		vchanrate = &d->rvchanrate;
473 		break;
474 	default:
475 		PCM_UNLOCK(d);
476 		return (EINVAL);
477 		break;
478 	}
479 
480 	if (vchancount < 1) {
481 		PCM_UNLOCK(d);
482 		return (EINVAL);
483 	}
484 
485 	PCM_ACQUIRE(d);
486 	PCM_UNLOCK(d);
487 
488 	if (direction == PCMDIR_PLAY)
489 		vchan_getparentchannel(d, &c, NULL);
490 	else
491 		vchan_getparentchannel(d, NULL, &c);
492 
493 	if (c == NULL) {
494 		PCM_RELEASE_QUICK(d);
495 		return (EINVAL);
496 	}
497 
498 	KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
499 	    __func__, direction, c->direction));
500 
501 	CHN_LOCK(c);
502 	newspd = c->speed;
503 	CHN_UNLOCK(c);
504 
505 	ret = sysctl_handle_int(oidp, &newspd, 0, req);
506 	if (ret != 0 || req->newptr == NULL) {
507 		PCM_RELEASE_QUICK(d);
508 		return (ret);
509 	}
510 
511 	if (newspd < 1 || newspd < feeder_rate_min ||
512 	    newspd > feeder_rate_max) {
513 		PCM_RELEASE_QUICK(d);
514 		return (EINVAL);
515 	}
516 
517 	CHN_LOCK(c);
518 
519 	if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) {
520 		if (CHN_STARTED(c)) {
521 			chn_abort(c);
522 			restart = 1;
523 		} else
524 			restart = 0;
525 
526 		if (feeder_rate_round) {
527 			caps = chn_getcaps(c);
528 			RANGE(newspd, caps->minspeed, caps->maxspeed);
529 			newspd = CHANNEL_SETSPEED(c->methods,
530 			    c->devinfo, newspd);
531 		}
532 
533 		ret = chn_reset(c, c->format, newspd);
534 		if (ret == 0) {
535 			*vchanrate = c->speed;
536 			if (restart != 0) {
537 				CHN_FOREACH(ch, c, children.busy) {
538 					CHN_LOCK(ch);
539 					if (VCHAN_SYNC_REQUIRED(ch))
540 						vchan_sync(ch);
541 					CHN_UNLOCK(ch);
542 				}
543 				c->flags |= CHN_F_DIRTY;
544 				ret = chn_start(c, 1);
545 			}
546 		}
547 	}
548 
549 	CHN_UNLOCK(c);
550 
551 	PCM_RELEASE_QUICK(d);
552 
553 	return (ret);
554 }
555 
556 static int
sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)557 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
558 {
559 	struct snddev_info *d;
560 	struct pcm_channel *c, *ch;
561 	uint32_t newfmt;
562 	int *vchanformat, vchancount, direction, ret, restart;
563 	char fmtstr[AFMTSTR_LEN];
564 
565 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
566 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
567 		return (EINVAL);
568 
569 	PCM_LOCK(d);
570 	PCM_WAIT(d);
571 
572 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
573 	case VCHAN_PLAY:
574 		direction = PCMDIR_PLAY;
575 		vchancount = d->pvchancount;
576 		vchanformat = &d->pvchanformat;
577 		break;
578 	case VCHAN_REC:
579 		direction = PCMDIR_REC;
580 		vchancount = d->rvchancount;
581 		vchanformat = &d->rvchanformat;
582 		break;
583 	default:
584 		PCM_UNLOCK(d);
585 		return (EINVAL);
586 		break;
587 	}
588 
589 	if (vchancount < 1) {
590 		PCM_UNLOCK(d);
591 		return (EINVAL);
592 	}
593 
594 	PCM_ACQUIRE(d);
595 	PCM_UNLOCK(d);
596 
597 	if (direction == PCMDIR_PLAY)
598 		vchan_getparentchannel(d, &c, NULL);
599 	else
600 		vchan_getparentchannel(d, NULL, &c);
601 
602 	if (c == NULL) {
603 		PCM_RELEASE_QUICK(d);
604 		return (EINVAL);
605 	}
606 
607 	KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
608 	    __func__, direction, c->direction));
609 
610 	CHN_LOCK(c);
611 
612 	bzero(fmtstr, sizeof(fmtstr));
613 
614 	if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format)
615 		strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr));
616 
617 	CHN_UNLOCK(c);
618 
619 	ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
620 	if (ret != 0 || req->newptr == NULL) {
621 		PCM_RELEASE_QUICK(d);
622 		return (ret);
623 	}
624 
625 	newfmt = snd_str2afmt(fmtstr);
626 	if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) {
627 		PCM_RELEASE_QUICK(d);
628 		return (EINVAL);
629 	}
630 
631 	CHN_LOCK(c);
632 
633 	if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) {
634 		if (CHN_STARTED(c)) {
635 			chn_abort(c);
636 			restart = 1;
637 		} else
638 			restart = 0;
639 
640 		ret = chn_reset(c, newfmt, c->speed);
641 		if (ret == 0) {
642 			*vchanformat = c->format;
643 			if (restart != 0) {
644 				CHN_FOREACH(ch, c, children.busy) {
645 					CHN_LOCK(ch);
646 					if (VCHAN_SYNC_REQUIRED(ch))
647 						vchan_sync(ch);
648 					CHN_UNLOCK(ch);
649 				}
650 				c->flags |= CHN_F_DIRTY;
651 				ret = chn_start(c, 1);
652 			}
653 		}
654 	}
655 
656 	CHN_UNLOCK(c);
657 
658 	PCM_RELEASE_QUICK(d);
659 
660 	return (ret);
661 }
662 
663 /* virtual channel interface */
664 
665 #define VCHAN_FMT_HINT(x)	((x) == PCMDIR_PLAY_VIRTUAL) ?		\
666 				"play.vchanformat" : "rec.vchanformat"
667 #define VCHAN_SPD_HINT(x)	((x) == PCMDIR_PLAY_VIRTUAL) ?		\
668 				"play.vchanrate" : "rec.vchanrate"
669 
670 int
vchan_create(struct pcm_channel * parent)671 vchan_create(struct pcm_channel *parent)
672 {
673 	struct snddev_info *d;
674 	struct pcm_channel *ch;
675 	struct pcmchan_caps *parent_caps;
676 	uint32_t vchanfmt, vchanspd;
677 	int ret, direction, r, save;
678 
679 	d = parent->parentsnddev;
680 
681 	PCM_BUSYASSERT(d);
682 	CHN_LOCKASSERT(parent);
683 
684 	if (!(parent->flags & CHN_F_BUSY))
685 		return (EBUSY);
686 
687 	if (!(parent->direction == PCMDIR_PLAY ||
688 	    parent->direction == PCMDIR_REC))
689 		return (EINVAL);
690 
691 	d = parent->parentsnddev;
692 
693 	CHN_UNLOCK(parent);
694 	PCM_LOCK(d);
695 
696 	if (parent->direction == PCMDIR_PLAY) {
697 		direction = PCMDIR_PLAY_VIRTUAL;
698 		vchanfmt = d->pvchanformat;
699 		vchanspd = d->pvchanrate;
700 	} else {
701 		direction = PCMDIR_REC_VIRTUAL;
702 		vchanfmt = d->rvchanformat;
703 		vchanspd = d->rvchanrate;
704 	}
705 
706 	/* create a new playback channel */
707 	ch = chn_init(d, parent, &vchan_class, direction, parent);
708 	if (ch == NULL) {
709 		PCM_UNLOCK(d);
710 		CHN_LOCK(parent);
711 		return (ENODEV);
712 	}
713 
714 	/* add us to our grandparent's channel list */
715 	pcm_chn_add(d, ch);
716 	PCM_UNLOCK(d);
717 
718 	CHN_LOCK(parent);
719 	/*
720 	 * Add us to our parent channel's children in reverse order
721 	 * so future destruction will pick the last (biggest number)
722 	 * channel.
723 	 */
724 	CHN_INSERT_SORT_DESCEND(parent, ch, children);
725 
726 	if (parent->flags & CHN_F_HAS_VCHAN)
727 		return (0);
728 
729 	parent->flags |= CHN_F_HAS_VCHAN;
730 
731 	ret = 0;
732 	parent_caps = chn_getcaps(parent);
733 	if (parent_caps == NULL)
734 		ret = EINVAL;
735 
736 	save = 0;
737 
738 	if (ret == 0 && vchanfmt == 0) {
739 		const char *vfmt;
740 
741 		CHN_UNLOCK(parent);
742 		r = resource_string_value(device_get_name(parent->dev),
743 		    device_get_unit(parent->dev), VCHAN_FMT_HINT(direction),
744 		    &vfmt);
745 		CHN_LOCK(parent);
746 		if (r != 0)
747 			vfmt = NULL;
748 		if (vfmt != NULL) {
749 			vchanfmt = snd_str2afmt(vfmt);
750 			if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN))
751 				vchanfmt = 0;
752 		}
753 		if (vchanfmt == 0)
754 			vchanfmt = VCHAN_DEFAULT_FORMAT;
755 		save = 1;
756 	}
757 
758 	if (ret == 0 && vchanspd == 0) {
759 		/*
760 		 * This is very sad. Few soundcards advertised as being
761 		 * able to do (insanely) higher/lower speed, but in
762 		 * reality, they simply can't. At least, we give user chance
763 		 * to set sane value via kernel hints or sysctl.
764 		 */
765 		CHN_UNLOCK(parent);
766 		r = resource_int_value(device_get_name(parent->dev),
767 		    device_get_unit(parent->dev), VCHAN_SPD_HINT(direction),
768 		    &vchanspd);
769 		CHN_LOCK(parent);
770 		if (r != 0) {
771 			/* No saved value, no hint, NOTHING. */
772 			vchanspd = VCHAN_DEFAULT_RATE;
773 			RANGE(vchanspd, parent_caps->minspeed,
774 			    parent_caps->maxspeed);
775 		}
776 		save = 1;
777 	}
778 
779 	if (ret == 0) {
780 		/*
781 		 * Limit the speed between feeder_rate_min <-> feeder_rate_max.
782 		 */
783 		RANGE(vchanspd, feeder_rate_min, feeder_rate_max);
784 
785 		if (feeder_rate_round) {
786 			RANGE(vchanspd, parent_caps->minspeed,
787 			    parent_caps->maxspeed);
788 			vchanspd = CHANNEL_SETSPEED(parent->methods,
789 			    parent->devinfo, vchanspd);
790 		}
791 
792 		ret = chn_reset(parent, vchanfmt, vchanspd);
793 	}
794 
795 	if (ret == 0 && save) {
796 		/*
797 		 * Save new value.
798 		 */
799 		if (direction == PCMDIR_PLAY_VIRTUAL) {
800 			d->pvchanformat = parent->format;
801 			d->pvchanrate = parent->speed;
802 		} else {
803 			d->rvchanformat = parent->format;
804 			d->rvchanrate = parent->speed;
805 		}
806 	}
807 
808 	/*
809 	 * If the parent channel supports digital format,
810 	 * enable passthrough mode.
811 	 */
812 	if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
813 		parent->flags &= ~CHN_F_VCHAN_DYNAMIC;
814 		parent->flags |= CHN_F_VCHAN_PASSTHROUGH;
815 	}
816 
817 	if (ret != 0) {
818 		CHN_REMOVE(parent, ch, children);
819 		parent->flags &= ~CHN_F_HAS_VCHAN;
820 		CHN_UNLOCK(parent);
821 		PCM_LOCK(d);
822 		if (pcm_chn_remove(d, ch) == 0) {
823 			PCM_UNLOCK(d);
824 			chn_kill(ch);
825 		} else
826 			PCM_UNLOCK(d);
827 		CHN_LOCK(parent);
828 	}
829 
830 	return (ret);
831 }
832 
833 int
vchan_destroy(struct pcm_channel * c)834 vchan_destroy(struct pcm_channel *c)
835 {
836 	struct pcm_channel *parent;
837 	struct snddev_info *d;
838 	int ret;
839 
840 	KASSERT(c != NULL && c->parentchannel != NULL &&
841 	    c->parentsnddev != NULL, ("%s(): invalid channel=%p",
842 	    __func__, c));
843 
844 	CHN_LOCKASSERT(c);
845 
846 	d = c->parentsnddev;
847 	parent = c->parentchannel;
848 
849 	PCM_BUSYASSERT(d);
850 	CHN_LOCKASSERT(parent);
851 
852 	CHN_UNLOCK(c);
853 
854 	if (!(parent->flags & CHN_F_BUSY))
855 		return (EBUSY);
856 
857 	if (CHN_EMPTY(parent, children))
858 		return (EINVAL);
859 
860 	/* remove us from our parent's children list */
861 	CHN_REMOVE(parent, c, children);
862 
863 	if (CHN_EMPTY(parent, children)) {
864 		parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
865 		chn_reset(parent, parent->format, parent->speed);
866 	}
867 
868 	CHN_UNLOCK(parent);
869 
870 	/* remove us from our grandparent's channel list */
871 	PCM_LOCK(d);
872 	ret = pcm_chn_remove(d, c);
873 	PCM_UNLOCK(d);
874 
875 	/* destroy ourselves */
876 	if (ret == 0)
877 		chn_kill(c);
878 
879 	CHN_LOCK(parent);
880 
881 	return (ret);
882 }
883 
884 int
885 #ifdef SND_DEBUG
vchan_passthrough(struct pcm_channel * c,const char * caller)886 vchan_passthrough(struct pcm_channel *c, const char *caller)
887 #else
888 vchan_sync(struct pcm_channel *c)
889 #endif
890 {
891 	int ret;
892 
893 	KASSERT(c != NULL && c->parentchannel != NULL &&
894 	    (c->flags & CHN_F_VIRTUAL),
895 	    ("%s(): invalid passthrough", __func__));
896 	CHN_LOCKASSERT(c);
897 	CHN_LOCKASSERT(c->parentchannel);
898 
899 	sndbuf_setspd(c->bufhard, c->parentchannel->speed);
900 	c->flags |= CHN_F_PASSTHROUGH;
901 	ret = feeder_chain(c);
902 	c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH);
903 	if (ret != 0)
904 		c->flags |= CHN_F_DIRTY;
905 
906 #ifdef SND_DEBUG
907 	if (snd_passthrough_verbose != 0) {
908 		char *devname, buf[CHN_NAMELEN];
909 
910 		devname = dsp_unit2name(buf, sizeof(buf), c);
911 		device_printf(c->dev,
912 		    "%s(%s/%s) %s() -> re-sync err=%d\n",
913 		    __func__, (devname != NULL) ? devname : "dspX", c->comm,
914 		    caller, ret);
915 	}
916 #endif
917 
918 	return (ret);
919 }
920 
921 int
vchan_setnew(struct snddev_info * d,int direction,int newcnt)922 vchan_setnew(struct snddev_info *d, int direction, int newcnt)
923 {
924 	struct pcm_channel *c, *ch, *nch;
925 	struct pcmchan_caps *caps;
926 	int i, err, vcnt;
927 
928 	PCM_BUSYASSERT(d);
929 
930 	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
931 	    (direction == PCMDIR_REC && d->reccount < 1))
932 		return (ENODEV);
933 
934 	if (!(d->flags & SD_F_AUTOVCHAN))
935 		return (EINVAL);
936 
937 	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
938 		return (E2BIG);
939 
940 	if (direction == PCMDIR_PLAY)
941 		vcnt = d->pvchancount;
942 	else if (direction == PCMDIR_REC)
943 		vcnt = d->rvchancount;
944 	else
945 		return (EINVAL);
946 
947 	if (newcnt > vcnt) {
948 		KASSERT((newcnt - 1) == vcnt,
949 		    ("bogus vchan_create() request newcnt=%d vcnt=%d",
950 		    newcnt, vcnt));
951 		/* add new vchans - find a parent channel first */
952 		ch = NULL;
953 		CHN_FOREACH(c, d, channels.pcm) {
954 			CHN_LOCK(c);
955 			if (c->direction == direction &&
956 			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
957 			    c->refcount < 1 &&
958 			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
959 				/*
960 				 * Reuse hw channel with vchans already
961 				 * created.
962 				 */
963 				if (c->flags & CHN_F_HAS_VCHAN) {
964 					ch = c;
965 					break;
966 				}
967 				/*
968 				 * No vchans ever created, look for
969 				 * channels with supported formats.
970 				 */
971 				caps = chn_getcaps(c);
972 				if (caps == NULL) {
973 					CHN_UNLOCK(c);
974 					continue;
975 				}
976 				for (i = 0; caps->fmtlist[i] != 0; i++) {
977 					if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
978 						break;
979 				}
980 				if (caps->fmtlist[i] != 0) {
981 					ch = c;
982 					break;
983 				}
984 			}
985 			CHN_UNLOCK(c);
986 		}
987 		if (ch == NULL)
988 			return (EBUSY);
989 		ch->flags |= CHN_F_BUSY;
990 		err = 0;
991 		while (err == 0 && newcnt > vcnt) {
992 			err = vchan_create(ch);
993 			if (err == 0)
994 				vcnt++;
995 			else if (err == E2BIG && newcnt > vcnt)
996 				device_printf(d->dev,
997 				    "%s: err=%d Maximum channel reached.\n",
998 				    __func__, err);
999 		}
1000 		if (vcnt == 0)
1001 			ch->flags &= ~CHN_F_BUSY;
1002 		CHN_UNLOCK(ch);
1003 		if (err != 0)
1004 			return (err);
1005 	} else if (newcnt < vcnt) {
1006 		CHN_FOREACH(c, d, channels.pcm) {
1007 			CHN_LOCK(c);
1008 			if (c->direction != direction ||
1009 			    CHN_EMPTY(c, children) ||
1010 			    !(c->flags & CHN_F_HAS_VCHAN)) {
1011 				CHN_UNLOCK(c);
1012 				continue;
1013 			}
1014 			CHN_FOREACH_SAFE(ch, c, nch, children) {
1015 				CHN_LOCK(ch);
1016 				if (vcnt == 1 && c->refcount > 0) {
1017 					CHN_UNLOCK(ch);
1018 					break;
1019 				}
1020 				if (!(ch->flags & CHN_F_BUSY) &&
1021 				    ch->refcount < 1) {
1022 					err = vchan_destroy(ch);
1023 					if (err == 0)
1024 						vcnt--;
1025 				} else
1026 					CHN_UNLOCK(ch);
1027 				if (vcnt == newcnt)
1028 					break;
1029 			}
1030 			CHN_UNLOCK(c);
1031 			break;
1032 		}
1033 	}
1034 
1035 	return (0);
1036 }
1037 
1038 void
vchan_setmaxauto(struct snddev_info * d,int num)1039 vchan_setmaxauto(struct snddev_info *d, int num)
1040 {
1041 	PCM_BUSYASSERT(d);
1042 
1043 	if (num < 0)
1044 		return;
1045 
1046 	if (num >= 0 && d->pvchancount > num)
1047 		(void)vchan_setnew(d, PCMDIR_PLAY, num);
1048 	else if (num > 0 && d->pvchancount == 0)
1049 		(void)vchan_setnew(d, PCMDIR_PLAY, 1);
1050 
1051 	if (num >= 0 && d->rvchancount > num)
1052 		(void)vchan_setnew(d, PCMDIR_REC, num);
1053 	else if (num > 0 && d->rvchancount == 0)
1054 		(void)vchan_setnew(d, PCMDIR_REC, 1);
1055 }
1056 
1057 static int
sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)1058 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
1059 {
1060 	struct snddev_info *d;
1061 	int i, v, error;
1062 
1063 	v = snd_maxautovchans;
1064 	error = sysctl_handle_int(oidp, &v, 0, req);
1065 	if (error == 0 && req->newptr != NULL) {
1066 		if (v < 0)
1067 			v = 0;
1068 		if (v > SND_MAXVCHANS)
1069 			v = SND_MAXVCHANS;
1070 		snd_maxautovchans = v;
1071 		for (i = 0; pcm_devclass != NULL &&
1072 		    i < devclass_get_maxunit(pcm_devclass); i++) {
1073 			d = devclass_get_softc(pcm_devclass, i);
1074 			if (!PCM_REGISTERED(d))
1075 				continue;
1076 			PCM_ACQUIRE_QUICK(d);
1077 			vchan_setmaxauto(d, v);
1078 			PCM_RELEASE_QUICK(d);
1079 		}
1080 	}
1081 	return (error);
1082 }
1083 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans,
1084     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
1085     sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
1086 
1087 void
vchan_initsys(device_t dev)1088 vchan_initsys(device_t dev)
1089 {
1090 	struct snddev_info *d;
1091 	int unit;
1092 
1093 	unit = device_get_unit(dev);
1094 	d = device_get_softc(dev);
1095 
1096 	/* Play */
1097 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1098 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1099 	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1100 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1101 	    sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
1102 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1103 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1104 	    OID_AUTO, "vchanmode",
1105 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1106 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1107 	    sysctl_dev_pcm_vchanmode, "A",
1108 	    "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1109 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1110 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1111 	    OID_AUTO, "vchanrate",
1112 	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1113 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1114 	    sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1115 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1116 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1117 	    OID_AUTO, "vchanformat",
1118 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1119 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1120 	    sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1121 	/* Rec */
1122 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1123 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1124 	    OID_AUTO, "vchans",
1125 	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1126 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1127 	    sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
1128 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1129 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1130 	    OID_AUTO, "vchanmode",
1131 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1132 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1133 	    sysctl_dev_pcm_vchanmode, "A",
1134 	    "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1135 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1136 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1137 	    OID_AUTO, "vchanrate",
1138 	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1139 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1140 	    sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1141 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1142 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1143 	    OID_AUTO, "vchanformat",
1144 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1145 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1146 	    sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1147 }
1148