xref: /freebsd/sys/dev/sound/pcm/vchan.c (revision 77ab4263bc42a5dcc102bbea39ce4f7f46b8d4f8)
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  *
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 /*
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_RWTUN,
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 int snd_maxautovchans = 16;
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 vchan_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 = vchan_setnew(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 		vchan_getparentchannel(d, &c, NULL);
384 	else
385 		vchan_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 		vchan_getparentchannel(d, &c, NULL);
486 	else
487 		vchan_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 		vchan_getparentchannel(d, &c, NULL);
595 	else
596 		vchan_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 = chn_init(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 	pcm_chn_add(d, ch);
712 	PCM_UNLOCK(d);
713 
714 	CHN_LOCK(parent);
715 	/*
716 	 * Add us to our parent channel's children in reverse order
717 	 * so future destruction will pick the last (biggest number)
718 	 * channel.
719 	 */
720 	CHN_INSERT_SORT_DESCEND(parent, ch, children);
721 
722 	if (parent->flags & CHN_F_HAS_VCHAN)
723 		return (0);
724 
725 	parent->flags |= CHN_F_HAS_VCHAN;
726 
727 	ret = 0;
728 	parent_caps = chn_getcaps(parent);
729 	if (parent_caps == NULL)
730 		ret = EINVAL;
731 
732 	save = 0;
733 
734 	if (ret == 0 && vchanfmt == 0) {
735 		const char *vfmt;
736 
737 		CHN_UNLOCK(parent);
738 		r = resource_string_value(device_get_name(parent->dev),
739 		    device_get_unit(parent->dev), VCHAN_FMT_HINT(direction),
740 		    &vfmt);
741 		CHN_LOCK(parent);
742 		if (r != 0)
743 			vfmt = NULL;
744 		if (vfmt != NULL) {
745 			vchanfmt = snd_str2afmt(vfmt);
746 			if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN))
747 				vchanfmt = 0;
748 		}
749 		if (vchanfmt == 0)
750 			vchanfmt = VCHAN_DEFAULT_FORMAT;
751 		save = 1;
752 	}
753 
754 	if (ret == 0 && vchanspd == 0) {
755 		/*
756 		 * This is very sad. Few soundcards advertised as being
757 		 * able to do (insanely) higher/lower speed, but in
758 		 * reality, they simply can't. At least, we give user chance
759 		 * to set sane value via kernel hints or sysctl.
760 		 */
761 		CHN_UNLOCK(parent);
762 		r = resource_int_value(device_get_name(parent->dev),
763 		    device_get_unit(parent->dev), VCHAN_SPD_HINT(direction),
764 		    &vchanspd);
765 		CHN_LOCK(parent);
766 		if (r != 0) {
767 			/*
768 			 * No saved value, no hint, NOTHING.
769 			 *
770 			 * Workaround for sb16 running
771 			 * poorly at 45k / 49k.
772 			 */
773 			switch (parent_caps->maxspeed) {
774 			case 45000:
775 			case 49000:
776 				vchanspd = 44100;
777 				break;
778 			default:
779 				vchanspd = VCHAN_DEFAULT_RATE;
780 				if (vchanspd > parent_caps->maxspeed)
781 					vchanspd = parent_caps->maxspeed;
782 				break;
783 			}
784 			if (vchanspd < parent_caps->minspeed)
785 				vchanspd = parent_caps->minspeed;
786 		}
787 		save = 1;
788 	}
789 
790 	if (ret == 0) {
791 		/*
792 		 * Limit the speed between feeder_rate_min <-> feeder_rate_max.
793 		 */
794 		if (vchanspd < feeder_rate_min)
795 			vchanspd = feeder_rate_min;
796 		if (vchanspd > feeder_rate_max)
797 			vchanspd = feeder_rate_max;
798 
799 		if (feeder_rate_round) {
800 			RANGE(vchanspd, parent_caps->minspeed,
801 			    parent_caps->maxspeed);
802 			vchanspd = CHANNEL_SETSPEED(parent->methods,
803 			    parent->devinfo, vchanspd);
804 		}
805 
806 		ret = chn_reset(parent, vchanfmt, vchanspd);
807 	}
808 
809 	if (ret == 0 && save) {
810 		/*
811 		 * Save new value.
812 		 */
813 		if (direction == PCMDIR_PLAY_VIRTUAL) {
814 			d->pvchanformat = parent->format;
815 			d->pvchanrate = parent->speed;
816 		} else {
817 			d->rvchanformat = parent->format;
818 			d->rvchanrate = parent->speed;
819 		}
820 	}
821 
822 	/*
823 	 * If the parent channel supports digital format,
824 	 * enable passthrough mode.
825 	 */
826 	if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
827 		parent->flags &= ~CHN_F_VCHAN_DYNAMIC;
828 		parent->flags |= CHN_F_VCHAN_PASSTHROUGH;
829 	}
830 
831 	if (ret != 0) {
832 		CHN_REMOVE(parent, ch, children);
833 		parent->flags &= ~CHN_F_HAS_VCHAN;
834 		CHN_UNLOCK(parent);
835 		PCM_LOCK(d);
836 		if (pcm_chn_remove(d, ch) == 0) {
837 			PCM_UNLOCK(d);
838 			chn_kill(ch);
839 		} else
840 			PCM_UNLOCK(d);
841 		CHN_LOCK(parent);
842 	}
843 
844 	return (ret);
845 }
846 
847 int
848 vchan_destroy(struct pcm_channel *c)
849 {
850 	struct pcm_channel *parent;
851 	struct snddev_info *d;
852 	int ret;
853 
854 	KASSERT(c != NULL && c->parentchannel != NULL &&
855 	    c->parentsnddev != NULL, ("%s(): invalid channel=%p",
856 	    __func__, c));
857 
858 	CHN_LOCKASSERT(c);
859 
860 	d = c->parentsnddev;
861 	parent = c->parentchannel;
862 
863 	PCM_BUSYASSERT(d);
864 	CHN_LOCKASSERT(parent);
865 
866 	CHN_UNLOCK(c);
867 
868 	if (!(parent->flags & CHN_F_BUSY))
869 		return (EBUSY);
870 
871 	if (CHN_EMPTY(parent, children))
872 		return (EINVAL);
873 
874 	/* remove us from our parent's children list */
875 	CHN_REMOVE(parent, c, children);
876 
877 	if (CHN_EMPTY(parent, children)) {
878 		parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
879 		chn_reset(parent, parent->format, parent->speed);
880 	}
881 
882 	CHN_UNLOCK(parent);
883 
884 	/* remove us from our grandparent's channel list */
885 	PCM_LOCK(d);
886 	ret = pcm_chn_remove(d, c);
887 	PCM_UNLOCK(d);
888 
889 	/* destroy ourselves */
890 	if (ret == 0)
891 		chn_kill(c);
892 
893 	CHN_LOCK(parent);
894 
895 	return (ret);
896 }
897 
898 int
899 #ifdef SND_DEBUG
900 vchan_passthrough(struct pcm_channel *c, const char *caller)
901 #else
902 vchan_sync(struct pcm_channel *c)
903 #endif
904 {
905 	int ret;
906 
907 	KASSERT(c != NULL && c->parentchannel != NULL &&
908 	    (c->flags & CHN_F_VIRTUAL),
909 	    ("%s(): invalid passthrough", __func__));
910 	CHN_LOCKASSERT(c);
911 	CHN_LOCKASSERT(c->parentchannel);
912 
913 	sndbuf_setspd(c->bufhard, c->parentchannel->speed);
914 	c->flags |= CHN_F_PASSTHROUGH;
915 	ret = feeder_chain(c);
916 	c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH);
917 	if (ret != 0)
918 		c->flags |= CHN_F_DIRTY;
919 
920 #ifdef SND_DEBUG
921 	if (snd_passthrough_verbose != 0) {
922 		char *devname, buf[CHN_NAMELEN];
923 
924 		devname = dsp_unit2name(buf, sizeof(buf), c);
925 		device_printf(c->dev,
926 		    "%s(%s/%s) %s() -> re-sync err=%d\n",
927 		    __func__, (devname != NULL) ? devname : "dspX", c->comm,
928 		    caller, ret);
929 	}
930 #endif
931 
932 	return (ret);
933 }
934 
935 int
936 vchan_setnew(struct snddev_info *d, int direction, int newcnt, int num)
937 {
938 	struct pcm_channel *c, *ch, *nch;
939 	struct pcmchan_caps *caps;
940 	int i, err, vcnt;
941 
942 	PCM_BUSYASSERT(d);
943 
944 	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
945 	    (direction == PCMDIR_REC && d->reccount < 1))
946 		return (ENODEV);
947 
948 	if (!(d->flags & SD_F_AUTOVCHAN))
949 		return (EINVAL);
950 
951 	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
952 		return (E2BIG);
953 
954 	if (direction == PCMDIR_PLAY)
955 		vcnt = d->pvchancount;
956 	else if (direction == PCMDIR_REC)
957 		vcnt = d->rvchancount;
958 	else
959 		return (EINVAL);
960 
961 	if (newcnt > vcnt) {
962 		KASSERT(num == -1 ||
963 		    (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
964 		    ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
965 		    num, newcnt, vcnt));
966 		/* add new vchans - find a parent channel first */
967 		ch = NULL;
968 		CHN_FOREACH(c, d, channels.pcm) {
969 			CHN_LOCK(c);
970 			if (c->direction == direction &&
971 			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
972 			    c->refcount < 1 &&
973 			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
974 				/*
975 				 * Reuse hw channel with vchans already
976 				 * created.
977 				 */
978 				if (c->flags & CHN_F_HAS_VCHAN) {
979 					ch = c;
980 					break;
981 				}
982 				/*
983 				 * No vchans ever created, look for
984 				 * channels with supported formats.
985 				 */
986 				caps = chn_getcaps(c);
987 				if (caps == NULL) {
988 					CHN_UNLOCK(c);
989 					continue;
990 				}
991 				for (i = 0; caps->fmtlist[i] != 0; i++) {
992 					if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
993 						break;
994 				}
995 				if (caps->fmtlist[i] != 0) {
996 					ch = c;
997 					break;
998 				}
999 			}
1000 			CHN_UNLOCK(c);
1001 		}
1002 		if (ch == NULL)
1003 			return (EBUSY);
1004 		ch->flags |= CHN_F_BUSY;
1005 		err = 0;
1006 		while (err == 0 && newcnt > vcnt) {
1007 			err = vchan_create(ch, num);
1008 			if (err == 0)
1009 				vcnt++;
1010 			else if (err == E2BIG && newcnt > vcnt)
1011 				device_printf(d->dev,
1012 				    "%s: err=%d Maximum channel reached.\n",
1013 				    __func__, err);
1014 		}
1015 		if (vcnt == 0)
1016 			ch->flags &= ~CHN_F_BUSY;
1017 		CHN_UNLOCK(ch);
1018 		if (err != 0)
1019 			return (err);
1020 	} else if (newcnt < vcnt) {
1021 		KASSERT(num == -1,
1022 		    ("bogus vchan_destroy() request num=%d", num));
1023 		CHN_FOREACH(c, d, channels.pcm) {
1024 			CHN_LOCK(c);
1025 			if (c->direction != direction ||
1026 			    CHN_EMPTY(c, children) ||
1027 			    !(c->flags & CHN_F_HAS_VCHAN)) {
1028 				CHN_UNLOCK(c);
1029 				continue;
1030 			}
1031 			CHN_FOREACH_SAFE(ch, c, nch, children) {
1032 				CHN_LOCK(ch);
1033 				if (vcnt == 1 && c->refcount > 0) {
1034 					CHN_UNLOCK(ch);
1035 					break;
1036 				}
1037 				if (!(ch->flags & CHN_F_BUSY) &&
1038 				    ch->refcount < 1) {
1039 					err = vchan_destroy(ch);
1040 					if (err == 0)
1041 						vcnt--;
1042 				} else
1043 					CHN_UNLOCK(ch);
1044 				if (vcnt == newcnt)
1045 					break;
1046 			}
1047 			CHN_UNLOCK(c);
1048 			break;
1049 		}
1050 	}
1051 
1052 	return (0);
1053 }
1054 
1055 void
1056 vchan_setmaxauto(struct snddev_info *d, int num)
1057 {
1058 	PCM_BUSYASSERT(d);
1059 
1060 	if (num < 0)
1061 		return;
1062 
1063 	if (num >= 0 && d->pvchancount > num)
1064 		(void)vchan_setnew(d, PCMDIR_PLAY, num, -1);
1065 	else if (num > 0 && d->pvchancount == 0)
1066 		(void)vchan_setnew(d, PCMDIR_PLAY, 1, -1);
1067 
1068 	if (num >= 0 && d->rvchancount > num)
1069 		(void)vchan_setnew(d, PCMDIR_REC, num, -1);
1070 	else if (num > 0 && d->rvchancount == 0)
1071 		(void)vchan_setnew(d, PCMDIR_REC, 1, -1);
1072 }
1073 
1074 static int
1075 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
1076 {
1077 	struct snddev_info *d;
1078 	int i, v, error;
1079 
1080 	v = snd_maxautovchans;
1081 	error = sysctl_handle_int(oidp, &v, 0, req);
1082 	if (error == 0 && req->newptr != NULL) {
1083 		if (v < 0)
1084 			v = 0;
1085 		if (v > SND_MAXVCHANS)
1086 			v = SND_MAXVCHANS;
1087 		snd_maxautovchans = v;
1088 		for (i = 0; pcm_devclass != NULL &&
1089 		    i < devclass_get_maxunit(pcm_devclass); i++) {
1090 			d = devclass_get_softc(pcm_devclass, i);
1091 			if (!PCM_REGISTERED(d))
1092 				continue;
1093 			PCM_ACQUIRE_QUICK(d);
1094 			vchan_setmaxauto(d, v);
1095 			PCM_RELEASE_QUICK(d);
1096 		}
1097 	}
1098 	return (error);
1099 }
1100 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans,
1101     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
1102     sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
1103 
1104 void
1105 vchan_initsys(device_t dev)
1106 {
1107 	struct snddev_info *d;
1108 	int unit;
1109 
1110 	unit = device_get_unit(dev);
1111 	d = device_get_softc(dev);
1112 
1113 	/* Play */
1114 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1115 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1116 	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1117 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1118 	    sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
1119 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1120 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1121 	    OID_AUTO, "vchanmode",
1122 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1123 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1124 	    sysctl_dev_pcm_vchanmode, "A",
1125 	    "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1126 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1127 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1128 	    OID_AUTO, "vchanrate",
1129 	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1130 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1131 	    sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1132 	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1133 	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1134 	    OID_AUTO, "vchanformat",
1135 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1136 	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1137 	    sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1138 	/* Rec */
1139 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1140 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1141 	    OID_AUTO, "vchans",
1142 	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1143 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1144 	    sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
1145 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1146 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1147 	    OID_AUTO, "vchanmode",
1148 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1149 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1150 	    sysctl_dev_pcm_vchanmode, "A",
1151 	    "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1152 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1153 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1154 	    OID_AUTO, "vchanrate",
1155 	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1156 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1157 	    sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1158 	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1159 	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1160 	    OID_AUTO, "vchanformat",
1161 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1162 	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1163 	    sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1164 }
1165