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
39SND_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
49static int snd_passthrough_verbose = 0;
50SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN,
51	&snd_passthrough_verbose, 0, "passthrough verbosity");
52
53#endif
54
55struct vchan_info {
56	struct pcm_channel *channel;
57	struct pcmchan_caps caps;
58	uint32_t fmtlist[FMTLIST_MAX];
59	int trigger;
60};
61
62static void *
63vchan_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
101static int
102vchan_free(kobj_t obj, void *data)
103{
104
105	free(data, M_DEVBUF);
106
107	return (0);
108}
109
110static int
111vchan_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
125static uint32_t
126vchan_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
137static int
138vchan_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
187static struct pcmchan_caps *
188vchan_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
234static struct pcmchan_matrix *
235vchan_getmatrix(kobj_t obj, void *data, uint32_t format)
236{
237
238	return (feeder_matrix_format_map(format));
239}
240
241static 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};
251CHANNEL_DECLARE(vchan);
252
253static void
254pcm_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
295static int
296sysctl_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
350static int
351sysctl_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)))
444static int
445sysctl_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
552static int
553sysctl_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
666int
667vchan_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
851int
852vchan_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
902int
903#ifdef SND_DEBUG
904vchan_passthrough(struct pcm_channel *c, const char *caller)
905#else
906vchan_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
939void
940vchan_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,
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", CTLTYPE_STRING | CTLFLAG_RWTUN,
957	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
958	    sysctl_dev_pcm_vchanmode, "A",
959	    "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
960	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
961	    SYSCTL_CHILDREN(d->play_sysctl_tree),
962	    OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RWTUN,
963	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
964	    sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
965	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
966	    SYSCTL_CHILDREN(d->play_sysctl_tree),
967	    OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RWTUN,
968	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
969	    sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
970	/* Rec */
971	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
972	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
973	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN,
974	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
975	    sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
976	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
977	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
978	    OID_AUTO, "vchanmode", CTLTYPE_STRING | CTLFLAG_RWTUN,
979	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
980	    sysctl_dev_pcm_vchanmode, "A",
981	    "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
982	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
983	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
984	    OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RWTUN,
985	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
986	    sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
987	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
988	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
989	    OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RWTUN,
990	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
991	    sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
992}
993