xref: /freebsd/sys/dev/sound/pcm/vchan.c (revision 3e5d0784b9b5296bda801add034b057ad68237f7)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 /* Almost entirely rewritten to add multi-format/channels mixing support. */
31 
32 #ifdef HAVE_KERNEL_OPTION_HEADERS
33 #include "opt_snd.h"
34 #endif
35 
36 #include <dev/sound/pcm/sound.h>
37 #include <dev/sound/pcm/vchan.h>
38 
39 SND_DECLARE_FILE("$FreeBSD$");
40 
41 /*
42  * [ac3 , dts , linear , 0, linear, 0]
43  */
44 #define FMTLIST_MAX		6
45 #define FMTLIST_OFFSET		4
46 #define DIGFMTS_MAX		2
47 
48 #ifdef SND_DEBUG
49 static int snd_passthrough_verbose = 0;
50 SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN,
51 	&snd_passthrough_verbose, 0, "passthrough verbosity");
52 
53 #endif
54 
55 struct vchan_info {
56 	struct pcm_channel *channel;
57 	struct pcmchan_caps caps;
58 	uint32_t fmtlist[FMTLIST_MAX];
59 	int trigger;
60 };
61 
62 static void *
63 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
64     struct pcm_channel *c, int dir)
65 {
66 	struct vchan_info *info;
67 	struct pcm_channel *p;
68 	uint32_t i, j, *fmtlist;
69 
70 	KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC,
71 	    ("vchan_init: bad direction"));
72 	KASSERT(c != NULL && c->parentchannel != NULL,
73 	    ("vchan_init: bad channels"));
74 
75 	info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO);
76 	info->channel = c;
77 	info->trigger = PCMTRIG_STOP;
78 	p = c->parentchannel;
79 
80 	CHN_LOCK(p);
81 
82 	fmtlist = chn_getcaps(p)->fmtlist;
83 	for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) {
84 		if (fmtlist[i] & AFMT_PASSTHROUGH)
85 			info->fmtlist[j++] = fmtlist[i];
86 	}
87 	if (p->format & AFMT_VCHAN)
88 		info->fmtlist[j] = p->format;
89 	else
90 		info->fmtlist[j] = VCHAN_DEFAULT_FORMAT;
91 	info->caps.fmtlist = info->fmtlist +
92 	    ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET);
93 
94 	CHN_UNLOCK(p);
95 
96 	c->flags |= CHN_F_VIRTUAL;
97 
98 	return (info);
99 }
100 
101 static int
102 vchan_free(kobj_t obj, void *data)
103 {
104 
105 	free(data, M_DEVBUF);
106 
107 	return (0);
108 }
109 
110 static int
111 vchan_setformat(kobj_t obj, void *data, uint32_t format)
112 {
113 	struct vchan_info *info;
114 
115 	info = data;
116 
117 	CHN_LOCKASSERT(info->channel);
118 
119 	if (!snd_fmtvalid(format, info->caps.fmtlist))
120 		return (-1);
121 
122 	return (0);
123 }
124 
125 static uint32_t
126 vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
127 {
128 	struct vchan_info *info;
129 
130 	info = data;
131 
132 	CHN_LOCKASSERT(info->channel);
133 
134 	return (info->caps.maxspeed);
135 }
136 
137 static int
138 vchan_trigger(kobj_t obj, void *data, int go)
139 {
140 	struct vchan_info *info;
141 	struct pcm_channel *c, *p;
142 	int ret, otrigger;
143 
144 	info = data;
145 
146 	if (!PCMTRIG_COMMON(go) || go == info->trigger)
147 		return (0);
148 
149 	c = info->channel;
150 	p = c->parentchannel;
151 	otrigger = info->trigger;
152 	info->trigger = go;
153 
154 	CHN_LOCKASSERT(c);
155 
156 	CHN_UNLOCK(c);
157 	CHN_LOCK(p);
158 
159 	switch (go) {
160 	case PCMTRIG_START:
161 		if (otrigger != PCMTRIG_START)
162 			CHN_INSERT_HEAD(p, c, children.busy);
163 		break;
164 	case PCMTRIG_STOP:
165 	case PCMTRIG_ABORT:
166 		if (otrigger == PCMTRIG_START)
167 			CHN_REMOVE(p, c, children.busy);
168 		break;
169 	default:
170 		break;
171 	}
172 
173 	ret = chn_notify(p, CHN_N_TRIGGER);
174 
175 	CHN_LOCK(c);
176 
177 	if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c))
178 		ret = vchan_sync(c);
179 
180 	CHN_UNLOCK(c);
181 	CHN_UNLOCK(p);
182 	CHN_LOCK(c);
183 
184 	return (ret);
185 }
186 
187 static struct pcmchan_caps *
188 vchan_getcaps(kobj_t obj, void *data)
189 {
190 	struct vchan_info *info;
191 	struct pcm_channel *c;
192 	uint32_t pformat, pspeed, pflags, i;
193 
194 	info = data;
195 	c = info->channel;
196 	pformat = c->parentchannel->format;
197 	pspeed = c->parentchannel->speed;
198 	pflags = c->parentchannel->flags;
199 
200 	CHN_LOCKASSERT(c);
201 
202 	if (pflags & CHN_F_VCHAN_DYNAMIC) {
203 		info->caps.fmtlist = info->fmtlist;
204 		if (pformat & AFMT_VCHAN) {
205 			for (i = 0; info->caps.fmtlist[i] != 0; i++) {
206 				if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH)
207 					continue;
208 				break;
209 			}
210 			info->caps.fmtlist[i] = pformat;
211 		}
212 		if (c->format & AFMT_PASSTHROUGH)
213 			info->caps.minspeed = c->speed;
214 		else
215 			info->caps.minspeed = pspeed;
216 		info->caps.maxspeed = info->caps.minspeed;
217 	} else {
218 		info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET;
219 		if (pformat & AFMT_VCHAN)
220 			info->caps.fmtlist[0] = pformat;
221 		else {
222 			device_printf(c->dev,
223 			    "%s(): invalid vchan format 0x%08x",
224 			    __func__, pformat);
225 			info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT;
226 		}
227 		info->caps.minspeed = pspeed;
228 		info->caps.maxspeed = info->caps.minspeed;
229 	}
230 
231 	return (&info->caps);
232 }
233 
234 static struct pcmchan_matrix *
235 vchan_getmatrix(kobj_t obj, void *data, uint32_t format)
236 {
237 
238 	return (feeder_matrix_format_map(format));
239 }
240 
241 static kobj_method_t vchan_methods[] = {
242 	KOBJMETHOD(channel_init,		vchan_init),
243 	KOBJMETHOD(channel_free,		vchan_free),
244 	KOBJMETHOD(channel_setformat,		vchan_setformat),
245 	KOBJMETHOD(channel_setspeed,		vchan_setspeed),
246 	KOBJMETHOD(channel_trigger,		vchan_trigger),
247 	KOBJMETHOD(channel_getcaps,		vchan_getcaps),
248 	KOBJMETHOD(channel_getmatrix,		vchan_getmatrix),
249 	KOBJMETHOD_END
250 };
251 CHANNEL_DECLARE(vchan);
252 
253 static void
254 pcm_getparentchannel(struct snddev_info *d,
255     struct pcm_channel **wrch, struct pcm_channel **rdch)
256 {
257 	struct pcm_channel **ch, *wch, *rch, *c;
258 
259 	KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__));
260 
261 	PCM_BUSYASSERT(d);
262 	PCM_UNLOCKASSERT(d);
263 
264 	wch = NULL;
265 	rch = NULL;
266 
267 	CHN_FOREACH(c, d, channels.pcm) {
268 		CHN_LOCK(c);
269 		ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch;
270 		if (c->flags & CHN_F_VIRTUAL) {
271 			/* Sanity check */
272 			if (*ch != NULL && *ch != c->parentchannel) {
273 				CHN_UNLOCK(c);
274 				*ch = NULL;
275 				break;
276 			}
277 		} else if (c->flags & CHN_F_HAS_VCHAN) {
278 			/* No way!! */
279 			if (*ch != NULL) {
280 				CHN_UNLOCK(c);
281 				*ch = NULL;
282 				break;
283 			}
284 			*ch = c;
285 		}
286 		CHN_UNLOCK(c);
287 	}
288 
289 	if (wrch != NULL)
290 		*wrch = wch;
291 	if (rdch != NULL)
292 		*rdch = rch;
293 }
294 
295 static int
296 sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
297 {
298 	struct snddev_info *d;
299 	int direction, vchancount;
300 	int err, cnt;
301 
302 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
303 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
304 		return (EINVAL);
305 
306 	PCM_LOCK(d);
307 	PCM_WAIT(d);
308 
309 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
310 	case VCHAN_PLAY:
311 		direction = PCMDIR_PLAY;
312 		vchancount = d->pvchancount;
313 		cnt = d->playcount;
314 		break;
315 	case VCHAN_REC:
316 		direction = PCMDIR_REC;
317 		vchancount = d->rvchancount;
318 		cnt = d->reccount;
319 		break;
320 	default:
321 		PCM_UNLOCK(d);
322 		return (EINVAL);
323 		break;
324 	}
325 
326 	if (cnt < 1) {
327 		PCM_UNLOCK(d);
328 		return (ENODEV);
329 	}
330 
331 	PCM_ACQUIRE(d);
332 	PCM_UNLOCK(d);
333 
334 	cnt = vchancount;
335 	err = sysctl_handle_int(oidp, &cnt, 0, req);
336 
337 	if (err == 0 && req->newptr != NULL && vchancount != cnt) {
338 		if (cnt < 0)
339 			cnt = 0;
340 		if (cnt > SND_MAXVCHANS)
341 			cnt = SND_MAXVCHANS;
342 		err = pcm_setvchans(d, direction, cnt, -1);
343 	}
344 
345 	PCM_RELEASE_QUICK(d);
346 
347 	return err;
348 }
349 
350 static int
351 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
352 {
353 	struct snddev_info *d;
354 	struct pcm_channel *c;
355 	uint32_t dflags;
356 	int direction, ret;
357 	char dtype[16];
358 
359 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
360 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
361 		return (EINVAL);
362 
363 	PCM_LOCK(d);
364 	PCM_WAIT(d);
365 
366 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
367 	case VCHAN_PLAY:
368 		direction = PCMDIR_PLAY;
369 		break;
370 	case VCHAN_REC:
371 		direction = PCMDIR_REC;
372 		break;
373 	default:
374 		PCM_UNLOCK(d);
375 		return (EINVAL);
376 		break;
377 	}
378 
379 	PCM_ACQUIRE(d);
380 	PCM_UNLOCK(d);
381 
382 	if (direction == PCMDIR_PLAY)
383 		pcm_getparentchannel(d, &c, NULL);
384 	else
385 		pcm_getparentchannel(d, NULL, &c);
386 
387 	if (c == NULL) {
388 		PCM_RELEASE_QUICK(d);
389 		return (EINVAL);
390 	}
391 
392 	KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
393 	    __func__, direction, c->direction));
394 
395 	CHN_LOCK(c);
396 	if (c->flags & CHN_F_VCHAN_PASSTHROUGH)
397 		strlcpy(dtype, "passthrough", sizeof(dtype));
398 	else if (c->flags & CHN_F_VCHAN_ADAPTIVE)
399 		strlcpy(dtype, "adaptive", sizeof(dtype));
400 	else
401 		strlcpy(dtype, "fixed", sizeof(dtype));
402 	CHN_UNLOCK(c);
403 
404 	ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req);
405 	if (ret == 0 && req->newptr != NULL) {
406 		if (strcasecmp(dtype, "passthrough") == 0 ||
407 		    strcmp(dtype, "1") == 0)
408 			dflags = CHN_F_VCHAN_PASSTHROUGH;
409 		else if (strcasecmp(dtype, "adaptive") == 0 ||
410 		    strcmp(dtype, "2") == 0)
411 			dflags = CHN_F_VCHAN_ADAPTIVE;
412 		else if (strcasecmp(dtype, "fixed") == 0 ||
413 		    strcmp(dtype, "0") == 0)
414 			dflags = 0;
415 		else {
416 			PCM_RELEASE_QUICK(d);
417 			return (EINVAL);
418 		}
419 		CHN_LOCK(c);
420 		if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) ||
421 		    (c->flags & CHN_F_PASSTHROUGH)) {
422 			CHN_UNLOCK(c);
423 			PCM_RELEASE_QUICK(d);
424 			return (0);
425 		}
426 		c->flags &= ~CHN_F_VCHAN_DYNAMIC;
427 		c->flags |= dflags;
428 		CHN_UNLOCK(c);
429 	}
430 
431 	PCM_RELEASE_QUICK(d);
432 
433 	return (ret);
434 }
435 
436 /*
437  * On the fly vchan rate/format settings
438  */
439 
440 #define VCHAN_ACCESSIBLE(c)	(!((c)->flags & (CHN_F_PASSTHROUGH |	\
441 				 CHN_F_EXCLUSIVE)) &&			\
442 				 (((c)->flags & CHN_F_VCHAN_DYNAMIC) ||	\
443 				 CHN_STOPPED(c)))
444 static int
445 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
446 {
447 	struct snddev_info *d;
448 	struct pcm_channel *c, *ch;
449 	struct pcmchan_caps *caps;
450 	int *vchanrate, vchancount, direction, ret, newspd, restart;
451 
452 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
453 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
454 		return (EINVAL);
455 
456 	PCM_LOCK(d);
457 	PCM_WAIT(d);
458 
459 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
460 	case VCHAN_PLAY:
461 		direction = PCMDIR_PLAY;
462 		vchancount = d->pvchancount;
463 		vchanrate = &d->pvchanrate;
464 		break;
465 	case VCHAN_REC:
466 		direction = PCMDIR_REC;
467 		vchancount = d->rvchancount;
468 		vchanrate = &d->rvchanrate;
469 		break;
470 	default:
471 		PCM_UNLOCK(d);
472 		return (EINVAL);
473 		break;
474 	}
475 
476 	if (vchancount < 1) {
477 		PCM_UNLOCK(d);
478 		return (EINVAL);
479 	}
480 
481 	PCM_ACQUIRE(d);
482 	PCM_UNLOCK(d);
483 
484 	if (direction == PCMDIR_PLAY)
485 		pcm_getparentchannel(d, &c, NULL);
486 	else
487 		pcm_getparentchannel(d, NULL, &c);
488 
489 	if (c == NULL) {
490 		PCM_RELEASE_QUICK(d);
491 		return (EINVAL);
492 	}
493 
494 	KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
495 	    __func__, direction, c->direction));
496 
497 	CHN_LOCK(c);
498 	newspd = c->speed;
499 	CHN_UNLOCK(c);
500 
501 	ret = sysctl_handle_int(oidp, &newspd, 0, req);
502 	if (ret != 0 || req->newptr == NULL) {
503 		PCM_RELEASE_QUICK(d);
504 		return (ret);
505 	}
506 
507 	if (newspd < 1 || newspd < feeder_rate_min ||
508 	    newspd > feeder_rate_max) {
509 		PCM_RELEASE_QUICK(d);
510 		return (EINVAL);
511 	}
512 
513 	CHN_LOCK(c);
514 
515 	if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) {
516 		if (CHN_STARTED(c)) {
517 			chn_abort(c);
518 			restart = 1;
519 		} else
520 			restart = 0;
521 
522 		if (feeder_rate_round) {
523 			caps = chn_getcaps(c);
524 			RANGE(newspd, caps->minspeed, caps->maxspeed);
525 			newspd = CHANNEL_SETSPEED(c->methods,
526 			    c->devinfo, newspd);
527 		}
528 
529 		ret = chn_reset(c, c->format, newspd);
530 		if (ret == 0) {
531 			*vchanrate = c->speed;
532 			if (restart != 0) {
533 				CHN_FOREACH(ch, c, children.busy) {
534 					CHN_LOCK(ch);
535 					if (VCHAN_SYNC_REQUIRED(ch))
536 						vchan_sync(ch);
537 					CHN_UNLOCK(ch);
538 				}
539 				c->flags |= CHN_F_DIRTY;
540 				ret = chn_start(c, 1);
541 			}
542 		}
543 	}
544 
545 	CHN_UNLOCK(c);
546 
547 	PCM_RELEASE_QUICK(d);
548 
549 	return (ret);
550 }
551 
552 static int
553 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
554 {
555 	struct snddev_info *d;
556 	struct pcm_channel *c, *ch;
557 	uint32_t newfmt;
558 	int *vchanformat, vchancount, direction, ret, restart;
559 	char fmtstr[AFMTSTR_LEN];
560 
561 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
562 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
563 		return (EINVAL);
564 
565 	PCM_LOCK(d);
566 	PCM_WAIT(d);
567 
568 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
569 	case VCHAN_PLAY:
570 		direction = PCMDIR_PLAY;
571 		vchancount = d->pvchancount;
572 		vchanformat = &d->pvchanformat;
573 		break;
574 	case VCHAN_REC:
575 		direction = PCMDIR_REC;
576 		vchancount = d->rvchancount;
577 		vchanformat = &d->rvchanformat;
578 		break;
579 	default:
580 		PCM_UNLOCK(d);
581 		return (EINVAL);
582 		break;
583 	}
584 
585 	if (vchancount < 1) {
586 		PCM_UNLOCK(d);
587 		return (EINVAL);
588 	}
589 
590 	PCM_ACQUIRE(d);
591 	PCM_UNLOCK(d);
592 
593 	if (direction == PCMDIR_PLAY)
594 		pcm_getparentchannel(d, &c, NULL);
595 	else
596 		pcm_getparentchannel(d, NULL, &c);
597 
598 	if (c == NULL) {
599 		PCM_RELEASE_QUICK(d);
600 		return (EINVAL);
601 	}
602 
603 	KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
604 	    __func__, direction, c->direction));
605 
606 	CHN_LOCK(c);
607 
608 	bzero(fmtstr, sizeof(fmtstr));
609 
610 	if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format)
611 		strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr));
612 
613 	CHN_UNLOCK(c);
614 
615 	ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
616 	if (ret != 0 || req->newptr == NULL) {
617 		PCM_RELEASE_QUICK(d);
618 		return (ret);
619 	}
620 
621 	newfmt = snd_str2afmt(fmtstr);
622 	if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) {
623 		PCM_RELEASE_QUICK(d);
624 		return (EINVAL);
625 	}
626 
627 	CHN_LOCK(c);
628 
629 	if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) {
630 		if (CHN_STARTED(c)) {
631 			chn_abort(c);
632 			restart = 1;
633 		} else
634 			restart = 0;
635 
636 		ret = chn_reset(c, newfmt, c->speed);
637 		if (ret == 0) {
638 			*vchanformat = c->format;
639 			if (restart != 0) {
640 				CHN_FOREACH(ch, c, children.busy) {
641 					CHN_LOCK(ch);
642 					if (VCHAN_SYNC_REQUIRED(ch))
643 						vchan_sync(ch);
644 					CHN_UNLOCK(ch);
645 				}
646 				c->flags |= CHN_F_DIRTY;
647 				ret = chn_start(c, 1);
648 			}
649 		}
650 	}
651 
652 	CHN_UNLOCK(c);
653 
654 	PCM_RELEASE_QUICK(d);
655 
656 	return (ret);
657 }
658 
659 /* virtual channel interface */
660 
661 #define VCHAN_FMT_HINT(x)	((x) == PCMDIR_PLAY_VIRTUAL) ?		\
662 				"play.vchanformat" : "rec.vchanformat"
663 #define VCHAN_SPD_HINT(x)	((x) == PCMDIR_PLAY_VIRTUAL) ?		\
664 				"play.vchanrate" : "rec.vchanrate"
665 
666 int
667 vchan_create(struct pcm_channel *parent, int num)
668 {
669 	struct snddev_info *d;
670 	struct pcm_channel *ch;
671 	struct pcmchan_caps *parent_caps;
672 	uint32_t vchanfmt, vchanspd;
673 	int ret, direction, r, save;
674 
675 	d = parent->parentsnddev;
676 
677 	PCM_BUSYASSERT(d);
678 	CHN_LOCKASSERT(parent);
679 
680 	if (!(parent->flags & CHN_F_BUSY))
681 		return (EBUSY);
682 
683 	if (!(parent->direction == PCMDIR_PLAY ||
684 	    parent->direction == PCMDIR_REC))
685 		return (EINVAL);
686 
687 	d = parent->parentsnddev;
688 
689 	CHN_UNLOCK(parent);
690 	PCM_LOCK(d);
691 
692 	if (parent->direction == PCMDIR_PLAY) {
693 		direction = PCMDIR_PLAY_VIRTUAL;
694 		vchanfmt = d->pvchanformat;
695 		vchanspd = d->pvchanrate;
696 	} else {
697 		direction = PCMDIR_REC_VIRTUAL;
698 		vchanfmt = d->rvchanformat;
699 		vchanspd = d->rvchanrate;
700 	}
701 
702 	/* create a new playback channel */
703 	ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent);
704 	if (ch == NULL) {
705 		PCM_UNLOCK(d);
706 		CHN_LOCK(parent);
707 		return (ENODEV);
708 	}
709 
710 	/* add us to our grandparent's channel list */
711 	ret = pcm_chn_add(d, ch);
712 	PCM_UNLOCK(d);
713 	if (ret != 0) {
714 		pcm_chn_destroy(ch);
715 		CHN_LOCK(parent);
716 		return (ret);
717 	}
718 
719 	CHN_LOCK(parent);
720 	/*
721 	 * Add us to our parent channel's children in reverse order
722 	 * so future destruction will pick the last (biggest number)
723 	 * channel.
724 	 */
725 	CHN_INSERT_SORT_DESCEND(parent, ch, children);
726 
727 	if (parent->flags & CHN_F_HAS_VCHAN)
728 		return (0);
729 
730 	parent->flags |= CHN_F_HAS_VCHAN;
731 
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 			/*
772 			 * No saved value, no hint, NOTHING.
773 			 *
774 			 * Workaround for sb16 running
775 			 * poorly at 45k / 49k.
776 			 */
777 			switch (parent_caps->maxspeed) {
778 			case 45000:
779 			case 49000:
780 				vchanspd = 44100;
781 				break;
782 			default:
783 				vchanspd = VCHAN_DEFAULT_RATE;
784 				if (vchanspd > parent_caps->maxspeed)
785 					vchanspd = parent_caps->maxspeed;
786 				break;
787 			}
788 			if (vchanspd < parent_caps->minspeed)
789 				vchanspd = parent_caps->minspeed;
790 		}
791 		save = 1;
792 	}
793 
794 	if (ret == 0) {
795 		/*
796 		 * Limit the speed between feeder_rate_min <-> feeder_rate_max.
797 		 */
798 		if (vchanspd < feeder_rate_min)
799 			vchanspd = feeder_rate_min;
800 		if (vchanspd > feeder_rate_max)
801 			vchanspd = feeder_rate_max;
802 
803 		if (feeder_rate_round) {
804 			RANGE(vchanspd, parent_caps->minspeed,
805 			    parent_caps->maxspeed);
806 			vchanspd = CHANNEL_SETSPEED(parent->methods,
807 			    parent->devinfo, vchanspd);
808 		}
809 
810 		ret = chn_reset(parent, vchanfmt, vchanspd);
811 	}
812 
813 	if (ret == 0 && save) {
814 		/*
815 		 * Save new value.
816 		 */
817 		if (direction == PCMDIR_PLAY_VIRTUAL) {
818 			d->pvchanformat = parent->format;
819 			d->pvchanrate = parent->speed;
820 		} else {
821 			d->rvchanformat = parent->format;
822 			d->rvchanrate = parent->speed;
823 		}
824 	}
825 
826 	/*
827 	 * If the parent channel supports digital format,
828 	 * enable passthrough mode.
829 	 */
830 	if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
831 		parent->flags &= ~CHN_F_VCHAN_DYNAMIC;
832 		parent->flags |= CHN_F_VCHAN_PASSTHROUGH;
833 	}
834 
835 	if (ret != 0) {
836 		CHN_REMOVE(parent, ch, children);
837 		parent->flags &= ~CHN_F_HAS_VCHAN;
838 		CHN_UNLOCK(parent);
839 		PCM_LOCK(d);
840 		if (pcm_chn_remove(d, ch) == 0) {
841 			PCM_UNLOCK(d);
842 			pcm_chn_destroy(ch);
843 		} else
844 			PCM_UNLOCK(d);
845 		CHN_LOCK(parent);
846 	}
847 
848 	return (ret);
849 }
850 
851 int
852 vchan_destroy(struct pcm_channel *c)
853 {
854 	struct pcm_channel *parent;
855 	struct snddev_info *d;
856 	int ret;
857 
858 	KASSERT(c != NULL && c->parentchannel != NULL &&
859 	    c->parentsnddev != NULL, ("%s(): invalid channel=%p",
860 	    __func__, c));
861 
862 	CHN_LOCKASSERT(c);
863 
864 	d = c->parentsnddev;
865 	parent = c->parentchannel;
866 
867 	PCM_BUSYASSERT(d);
868 	CHN_LOCKASSERT(parent);
869 
870 	CHN_UNLOCK(c);
871 
872 	if (!(parent->flags & CHN_F_BUSY))
873 		return (EBUSY);
874 
875 	if (CHN_EMPTY(parent, children))
876 		return (EINVAL);
877 
878 	/* remove us from our parent's children list */
879 	CHN_REMOVE(parent, c, children);
880 
881 	if (CHN_EMPTY(parent, children)) {
882 		parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
883 		chn_reset(parent, parent->format, parent->speed);
884 	}
885 
886 	CHN_UNLOCK(parent);
887 
888 	/* remove us from our grandparent's channel list */
889 	PCM_LOCK(d);
890 	ret = pcm_chn_remove(d, c);
891 	PCM_UNLOCK(d);
892 
893 	/* destroy ourselves */
894 	if (ret == 0)
895 		ret = pcm_chn_destroy(c);
896 
897 	CHN_LOCK(parent);
898 
899 	return (ret);
900 }
901 
902 int
903 #ifdef SND_DEBUG
904 vchan_passthrough(struct pcm_channel *c, const char *caller)
905 #else
906 vchan_sync(struct pcm_channel *c)
907 #endif
908 {
909 	int ret;
910 
911 	KASSERT(c != NULL && c->parentchannel != NULL &&
912 	    (c->flags & CHN_F_VIRTUAL),
913 	    ("%s(): invalid passthrough", __func__));
914 	CHN_LOCKASSERT(c);
915 	CHN_LOCKASSERT(c->parentchannel);
916 
917 	sndbuf_setspd(c->bufhard, c->parentchannel->speed);
918 	c->flags |= CHN_F_PASSTHROUGH;
919 	ret = feeder_chain(c);
920 	c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH);
921 	if (ret != 0)
922 		c->flags |= CHN_F_DIRTY;
923 
924 #ifdef SND_DEBUG
925 	if (snd_passthrough_verbose != 0) {
926 		char *devname, buf[CHN_NAMELEN];
927 
928 		devname = dsp_unit2name(buf, sizeof(buf), c->unit);
929 		device_printf(c->dev,
930 		    "%s(%s/%s) %s() -> re-sync err=%d\n",
931 		    __func__, (devname != NULL) ? devname : "dspX", c->comm,
932 		    caller, ret);
933 	}
934 #endif
935 
936 	return (ret);
937 }
938 
939 void
940 vchan_initsys(device_t dev)
941 {
942 	struct snddev_info *d;
943 	int unit;
944 
945 	unit = device_get_unit(dev);
946 	d = device_get_softc(dev);
947 
948 	/* Play */
949 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
950 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
951 	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
952 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
953 	    sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
954 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
955 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
956 	    OID_AUTO, "vchanmode",
957 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
958 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
959 	    sysctl_dev_pcm_vchanmode, "A",
960 	    "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
961 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
962 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
963 	    OID_AUTO, "vchanrate",
964 	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
965 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
966 	    sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
967 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
968 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
969 	    OID_AUTO, "vchanformat",
970 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
971 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
972 	    sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
973 	/* Rec */
974 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
975 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
976 	    OID_AUTO, "vchans",
977 	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
978 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
979 	    sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
980 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
981 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
982 	    OID_AUTO, "vchanmode",
983 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
984 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
985 	    sysctl_dev_pcm_vchanmode, "A",
986 	    "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
987 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
988 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
989 	    OID_AUTO, "vchanrate",
990 	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
991 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
992 	    sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
993 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
994 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
995 	    OID_AUTO, "vchanformat",
996 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
997 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
998 	    sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
999 }
1000