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