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
51static int snd_passthrough_verbose = 0;
52SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN,
53	&snd_passthrough_verbose, 0, "passthrough verbosity");
54
55#endif
56
57struct vchan_info {
58	struct pcm_channel *channel;
59	struct pcmchan_caps caps;
60	uint32_t fmtlist[FMTLIST_MAX];
61	int trigger;
62};
63
64int snd_maxautovchans = 16;
65
66static void *
67vchan_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
105static int
106vchan_free(kobj_t obj, void *data)
107{
108
109	free(data, M_DEVBUF);
110
111	return (0);
112}
113
114static int
115vchan_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
129static uint32_t
130vchan_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
141static int
142vchan_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
191static struct pcmchan_caps *
192vchan_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
238static struct pcmchan_matrix *
239vchan_getmatrix(kobj_t obj, void *data, uint32_t format)
240{
241
242	return (feeder_matrix_format_map(format));
243}
244
245static 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};
255CHANNEL_DECLARE(vchan);
256
257static void
258vchan_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
299static int
300sysctl_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
354static int
355sysctl_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)))
448static int
449sysctl_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
556static int
557sysctl_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
670int
671vchan_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			/*
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			chn_kill(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		chn_kill(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);
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
939int
940vchan_setnew(struct snddev_info *d, int direction, int newcnt)
941{
942	struct pcm_channel *c, *ch, *nch;
943	struct pcmchan_caps *caps;
944	int i, err, vcnt;
945
946	PCM_BUSYASSERT(d);
947
948	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
949	    (direction == PCMDIR_REC && d->reccount < 1))
950		return (ENODEV);
951
952	if (!(d->flags & SD_F_AUTOVCHAN))
953		return (EINVAL);
954
955	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
956		return (E2BIG);
957
958	if (direction == PCMDIR_PLAY)
959		vcnt = d->pvchancount;
960	else if (direction == PCMDIR_REC)
961		vcnt = d->rvchancount;
962	else
963		return (EINVAL);
964
965	if (newcnt > vcnt) {
966		KASSERT((newcnt - 1) == vcnt,
967		    ("bogus vchan_create() request newcnt=%d vcnt=%d",
968		    newcnt, vcnt));
969		/* add new vchans - find a parent channel first */
970		ch = NULL;
971		CHN_FOREACH(c, d, channels.pcm) {
972			CHN_LOCK(c);
973			if (c->direction == direction &&
974			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
975			    c->refcount < 1 &&
976			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
977				/*
978				 * Reuse hw channel with vchans already
979				 * created.
980				 */
981				if (c->flags & CHN_F_HAS_VCHAN) {
982					ch = c;
983					break;
984				}
985				/*
986				 * No vchans ever created, look for
987				 * channels with supported formats.
988				 */
989				caps = chn_getcaps(c);
990				if (caps == NULL) {
991					CHN_UNLOCK(c);
992					continue;
993				}
994				for (i = 0; caps->fmtlist[i] != 0; i++) {
995					if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
996						break;
997				}
998				if (caps->fmtlist[i] != 0) {
999					ch = c;
1000					break;
1001				}
1002			}
1003			CHN_UNLOCK(c);
1004		}
1005		if (ch == NULL)
1006			return (EBUSY);
1007		ch->flags |= CHN_F_BUSY;
1008		err = 0;
1009		while (err == 0 && newcnt > vcnt) {
1010			err = vchan_create(ch);
1011			if (err == 0)
1012				vcnt++;
1013			else if (err == E2BIG && newcnt > vcnt)
1014				device_printf(d->dev,
1015				    "%s: err=%d Maximum channel reached.\n",
1016				    __func__, err);
1017		}
1018		if (vcnt == 0)
1019			ch->flags &= ~CHN_F_BUSY;
1020		CHN_UNLOCK(ch);
1021		if (err != 0)
1022			return (err);
1023	} else if (newcnt < vcnt) {
1024		CHN_FOREACH(c, d, channels.pcm) {
1025			CHN_LOCK(c);
1026			if (c->direction != direction ||
1027			    CHN_EMPTY(c, children) ||
1028			    !(c->flags & CHN_F_HAS_VCHAN)) {
1029				CHN_UNLOCK(c);
1030				continue;
1031			}
1032			CHN_FOREACH_SAFE(ch, c, nch, children) {
1033				CHN_LOCK(ch);
1034				if (vcnt == 1 && c->refcount > 0) {
1035					CHN_UNLOCK(ch);
1036					break;
1037				}
1038				if (!(ch->flags & CHN_F_BUSY) &&
1039				    ch->refcount < 1) {
1040					err = vchan_destroy(ch);
1041					if (err == 0)
1042						vcnt--;
1043				} else
1044					CHN_UNLOCK(ch);
1045				if (vcnt == newcnt)
1046					break;
1047			}
1048			CHN_UNLOCK(c);
1049			break;
1050		}
1051	}
1052
1053	return (0);
1054}
1055
1056void
1057vchan_setmaxauto(struct snddev_info *d, int num)
1058{
1059	PCM_BUSYASSERT(d);
1060
1061	if (num < 0)
1062		return;
1063
1064	if (num >= 0 && d->pvchancount > num)
1065		(void)vchan_setnew(d, PCMDIR_PLAY, num);
1066	else if (num > 0 && d->pvchancount == 0)
1067		(void)vchan_setnew(d, PCMDIR_PLAY, 1);
1068
1069	if (num >= 0 && d->rvchancount > num)
1070		(void)vchan_setnew(d, PCMDIR_REC, num);
1071	else if (num > 0 && d->rvchancount == 0)
1072		(void)vchan_setnew(d, PCMDIR_REC, 1);
1073}
1074
1075static int
1076sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
1077{
1078	struct snddev_info *d;
1079	int i, v, error;
1080
1081	v = snd_maxautovchans;
1082	error = sysctl_handle_int(oidp, &v, 0, req);
1083	if (error == 0 && req->newptr != NULL) {
1084		if (v < 0)
1085			v = 0;
1086		if (v > SND_MAXVCHANS)
1087			v = SND_MAXVCHANS;
1088		snd_maxautovchans = v;
1089		for (i = 0; pcm_devclass != NULL &&
1090		    i < devclass_get_maxunit(pcm_devclass); i++) {
1091			d = devclass_get_softc(pcm_devclass, i);
1092			if (!PCM_REGISTERED(d))
1093				continue;
1094			PCM_ACQUIRE_QUICK(d);
1095			vchan_setmaxauto(d, v);
1096			PCM_RELEASE_QUICK(d);
1097		}
1098	}
1099	return (error);
1100}
1101SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans,
1102    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
1103    sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
1104
1105void
1106vchan_initsys(device_t dev)
1107{
1108	struct snddev_info *d;
1109	int unit;
1110
1111	unit = device_get_unit(dev);
1112	d = device_get_softc(dev);
1113
1114	/* Play */
1115	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1116	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1117	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1118	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1119	    sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
1120	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1121	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1122	    OID_AUTO, "vchanmode",
1123	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1124	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1125	    sysctl_dev_pcm_vchanmode, "A",
1126	    "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1127	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1128	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1129	    OID_AUTO, "vchanrate",
1130	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1131	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1132	    sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1133	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1134	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1135	    OID_AUTO, "vchanformat",
1136	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1137	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1138	    sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1139	/* Rec */
1140	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1141	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1142	    OID_AUTO, "vchans",
1143	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1144	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1145	    sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
1146	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1147	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1148	    OID_AUTO, "vchanmode",
1149	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1150	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1151	    sysctl_dev_pcm_vchanmode, "A",
1152	    "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1153	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1154	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1155	    OID_AUTO, "vchanrate",
1156	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1157	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1158	    sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1159	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1160	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1161	    OID_AUTO, "vchanformat",
1162	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1163	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1164	    sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1165}
1166