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-2025 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
51 static int snd_passthrough_verbose = 0;
52 SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN,
53 &snd_passthrough_verbose, 0, "passthrough verbosity");
54
55 #endif
56
57 struct vchan_info {
58 struct pcm_channel *channel;
59 struct pcmchan_caps caps;
60 uint32_t fmtlist[FMTLIST_MAX];
61 int trigger;
62 };
63
64 bool snd_vchans_enable = true;
65
66 static void *
vchan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)67 vchan_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
105 static int
vchan_free(kobj_t obj,void * data)106 vchan_free(kobj_t obj, void *data)
107 {
108
109 free(data, M_DEVBUF);
110
111 return (0);
112 }
113
114 static int
vchan_setformat(kobj_t obj,void * data,uint32_t format)115 vchan_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
129 static uint32_t
vchan_setspeed(kobj_t obj,void * data,uint32_t speed)130 vchan_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
141 static int
vchan_trigger(kobj_t obj,void * data,int go)142 vchan_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 c = info->channel;
150 p = c->parentchannel;
151
152 CHN_LOCKASSERT(c);
153 if (!PCMTRIG_COMMON(go) || go == info->trigger)
154 return (0);
155
156 CHN_UNLOCK(c);
157 CHN_LOCK(p);
158
159 otrigger = info->trigger;
160 info->trigger = go;
161
162 switch (go) {
163 case PCMTRIG_START:
164 if (otrigger != PCMTRIG_START)
165 CHN_INSERT_HEAD(p, c, children.busy);
166 break;
167 case PCMTRIG_STOP:
168 case PCMTRIG_ABORT:
169 if (otrigger == PCMTRIG_START)
170 CHN_REMOVE(p, c, children.busy);
171 break;
172 default:
173 break;
174 }
175
176 ret = chn_notify(p, CHN_N_TRIGGER);
177
178 CHN_LOCK(c);
179
180 if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c))
181 ret = vchan_sync(c);
182
183 CHN_UNLOCK(c);
184 CHN_UNLOCK(p);
185 CHN_LOCK(c);
186
187 return (ret);
188 }
189
190 static struct pcmchan_caps *
vchan_getcaps(kobj_t obj,void * data)191 vchan_getcaps(kobj_t obj, void *data)
192 {
193 struct vchan_info *info;
194 struct pcm_channel *c;
195 uint32_t pformat, pspeed, pflags, i;
196
197 info = data;
198 c = info->channel;
199 pformat = c->parentchannel->format;
200 pspeed = c->parentchannel->speed;
201 pflags = c->parentchannel->flags;
202
203 CHN_LOCKASSERT(c);
204
205 if (pflags & CHN_F_VCHAN_DYNAMIC) {
206 info->caps.fmtlist = info->fmtlist;
207 if (pformat & AFMT_VCHAN) {
208 for (i = 0; info->caps.fmtlist[i] != 0; i++) {
209 if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH)
210 continue;
211 break;
212 }
213 info->caps.fmtlist[i] = pformat;
214 }
215 if (c->format & AFMT_PASSTHROUGH)
216 info->caps.minspeed = c->speed;
217 else
218 info->caps.minspeed = pspeed;
219 info->caps.maxspeed = info->caps.minspeed;
220 } else {
221 info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET;
222 if (pformat & AFMT_VCHAN)
223 info->caps.fmtlist[0] = pformat;
224 else {
225 device_printf(c->dev,
226 "%s(): invalid vchan format 0x%08x",
227 __func__, pformat);
228 info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT;
229 }
230 info->caps.minspeed = pspeed;
231 info->caps.maxspeed = info->caps.minspeed;
232 }
233
234 return (&info->caps);
235 }
236
237 static struct pcmchan_matrix *
vchan_getmatrix(kobj_t obj,void * data,uint32_t format)238 vchan_getmatrix(kobj_t obj, void *data, uint32_t format)
239 {
240
241 return (feeder_matrix_format_map(format));
242 }
243
244 static kobj_method_t vchan_methods[] = {
245 KOBJMETHOD(channel_init, vchan_init),
246 KOBJMETHOD(channel_free, vchan_free),
247 KOBJMETHOD(channel_setformat, vchan_setformat),
248 KOBJMETHOD(channel_setspeed, vchan_setspeed),
249 KOBJMETHOD(channel_trigger, vchan_trigger),
250 KOBJMETHOD(channel_getcaps, vchan_getcaps),
251 KOBJMETHOD(channel_getmatrix, vchan_getmatrix),
252 KOBJMETHOD_END
253 };
254 CHANNEL_DECLARE(vchan);
255
256 static int
sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)257 sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
258 {
259 struct snddev_info *d;
260 int err, enabled, flag;
261
262 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
263 if (!PCM_REGISTERED(d))
264 return (EINVAL);
265
266 PCM_LOCK(d);
267 PCM_WAIT(d);
268
269 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
270 case VCHAN_PLAY:
271 /* Exit if we do not support this direction. */
272 if (d->playcount < 1) {
273 PCM_UNLOCK(d);
274 return (ENODEV);
275 }
276 flag = SD_F_PVCHANS;
277 break;
278 case VCHAN_REC:
279 if (d->reccount < 1) {
280 PCM_UNLOCK(d);
281 return (ENODEV);
282 }
283 flag = SD_F_RVCHANS;
284 break;
285 default:
286 PCM_UNLOCK(d);
287 return (EINVAL);
288 }
289
290 enabled = (d->flags & flag) != 0;
291
292 PCM_ACQUIRE(d);
293 PCM_UNLOCK(d);
294
295 err = sysctl_handle_int(oidp, &enabled, 0, req);
296 if (err != 0 || req->newptr == NULL) {
297 PCM_RELEASE_QUICK(d);
298 return (err);
299 }
300
301 if (enabled <= 0)
302 d->flags &= ~flag;
303 else
304 d->flags |= flag;
305
306 PCM_RELEASE_QUICK(d);
307
308 return (0);
309 }
310
311 static int
sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)312 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
313 {
314 struct snddev_info *d;
315 struct pcm_channel *c;
316 uint32_t dflags;
317 int *vchanmode, direction, ret;
318 char dtype[16];
319
320 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
321 if (!PCM_REGISTERED(d))
322 return (EINVAL);
323
324 PCM_LOCK(d);
325 PCM_WAIT(d);
326
327 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
328 case VCHAN_PLAY:
329 if ((d->flags & SD_F_PVCHANS) == 0) {
330 PCM_UNLOCK(d);
331 return (ENODEV);
332 }
333 direction = PCMDIR_PLAY;
334 vchanmode = &d->pvchanmode;
335 break;
336 case VCHAN_REC:
337 if ((d->flags & SD_F_RVCHANS) == 0) {
338 PCM_UNLOCK(d);
339 return (ENODEV);
340 }
341 direction = PCMDIR_REC;
342 vchanmode = &d->rvchanmode;
343 break;
344 default:
345 PCM_UNLOCK(d);
346 return (EINVAL);
347 }
348
349 PCM_ACQUIRE(d);
350 PCM_UNLOCK(d);
351
352 if (*vchanmode & CHN_F_VCHAN_PASSTHROUGH)
353 strlcpy(dtype, "passthrough", sizeof(dtype));
354 else if (*vchanmode & CHN_F_VCHAN_ADAPTIVE)
355 strlcpy(dtype, "adaptive", sizeof(dtype));
356 else
357 strlcpy(dtype, "fixed", sizeof(dtype));
358
359 ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req);
360 if (ret != 0 || req->newptr == NULL) {
361 PCM_RELEASE_QUICK(d);
362 return (ret);
363 }
364
365 if (strcasecmp(dtype, "passthrough") == 0 || strcmp(dtype, "1") == 0)
366 dflags = CHN_F_VCHAN_PASSTHROUGH;
367 else if (strcasecmp(dtype, "adaptive") == 0 || strcmp(dtype, "2") == 0)
368 dflags = CHN_F_VCHAN_ADAPTIVE;
369 else if (strcasecmp(dtype, "fixed") == 0 || strcmp(dtype, "0") == 0)
370 dflags = 0;
371 else {
372 PCM_RELEASE_QUICK(d);
373 return (EINVAL);
374 }
375
376 CHN_FOREACH(c, d, channels.pcm.primary) {
377 CHN_LOCK(c);
378 if (c->direction != direction ||
379 dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) ||
380 (c->flags & CHN_F_PASSTHROUGH)) {
381 CHN_UNLOCK(c);
382 continue;
383 }
384 c->flags &= ~CHN_F_VCHAN_DYNAMIC;
385 c->flags |= dflags;
386 CHN_UNLOCK(c);
387 *vchanmode = dflags;
388 }
389
390 PCM_RELEASE_QUICK(d);
391
392 return (ret);
393 }
394
395 /*
396 * On the fly vchan rate/format settings
397 */
398
399 #define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \
400 CHN_F_EXCLUSIVE)) && \
401 (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \
402 CHN_STOPPED(c)))
403 static int
sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)404 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
405 {
406 struct snddev_info *d;
407 struct pcm_channel *c, *ch;
408 struct pcmchan_caps *caps;
409 int *vchanrate, direction, ret, newspd, restart;
410
411 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
412 if (!PCM_REGISTERED(d))
413 return (EINVAL);
414
415 PCM_LOCK(d);
416 PCM_WAIT(d);
417
418 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
419 case VCHAN_PLAY:
420 if ((d->flags & SD_F_PVCHANS) == 0) {
421 PCM_UNLOCK(d);
422 return (ENODEV);
423 }
424 direction = PCMDIR_PLAY;
425 vchanrate = &d->pvchanrate;
426 break;
427 case VCHAN_REC:
428 if ((d->flags & SD_F_RVCHANS) == 0) {
429 PCM_UNLOCK(d);
430 return (ENODEV);
431 }
432 direction = PCMDIR_REC;
433 vchanrate = &d->rvchanrate;
434 break;
435 default:
436 PCM_UNLOCK(d);
437 return (EINVAL);
438 }
439
440 PCM_ACQUIRE(d);
441 PCM_UNLOCK(d);
442
443 newspd = *vchanrate;
444
445 ret = sysctl_handle_int(oidp, &newspd, 0, req);
446 if (ret != 0 || req->newptr == NULL) {
447 PCM_RELEASE_QUICK(d);
448 return (ret);
449 }
450
451 if (newspd < feeder_rate_min || newspd > feeder_rate_max) {
452 PCM_RELEASE_QUICK(d);
453 return (EINVAL);
454 }
455
456 CHN_FOREACH(c, d, channels.pcm.primary) {
457 CHN_LOCK(c);
458 if (c->direction != direction) {
459 CHN_UNLOCK(c);
460 continue;
461 }
462
463 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) {
464 if (CHN_STARTED(c)) {
465 chn_abort(c);
466 restart = 1;
467 } else
468 restart = 0;
469
470 if (feeder_rate_round) {
471 caps = chn_getcaps(c);
472 RANGE(newspd, caps->minspeed, caps->maxspeed);
473 newspd = CHANNEL_SETSPEED(c->methods,
474 c->devinfo, newspd);
475 }
476
477 ret = chn_reset(c, c->format, newspd);
478 if (ret == 0) {
479 if (restart != 0) {
480 CHN_FOREACH(ch, c, children.busy) {
481 CHN_LOCK(ch);
482 if (VCHAN_SYNC_REQUIRED(ch))
483 vchan_sync(ch);
484 CHN_UNLOCK(ch);
485 }
486 c->flags |= CHN_F_DIRTY;
487 ret = chn_start(c, 1);
488 }
489 }
490 }
491 *vchanrate = c->speed;
492
493 CHN_UNLOCK(c);
494 }
495
496 PCM_RELEASE_QUICK(d);
497
498 return (ret);
499 }
500
501 static int
sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)502 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
503 {
504 struct snddev_info *d;
505 struct pcm_channel *c, *ch;
506 uint32_t newfmt;
507 int *vchanformat, direction, ret, restart;
508 char fmtstr[AFMTSTR_LEN];
509
510 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
511 if (!PCM_REGISTERED(d))
512 return (EINVAL);
513
514 PCM_LOCK(d);
515 PCM_WAIT(d);
516
517 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
518 case VCHAN_PLAY:
519 if ((d->flags & SD_F_PVCHANS) == 0) {
520 PCM_UNLOCK(d);
521 return (ENODEV);
522 }
523 direction = PCMDIR_PLAY;
524 vchanformat = &d->pvchanformat;
525 break;
526 case VCHAN_REC:
527 if ((d->flags & SD_F_RVCHANS) == 0) {
528 PCM_UNLOCK(d);
529 return (ENODEV);
530 }
531 direction = PCMDIR_REC;
532 vchanformat = &d->rvchanformat;
533 break;
534 default:
535 PCM_UNLOCK(d);
536 return (EINVAL);
537 }
538
539 PCM_ACQUIRE(d);
540 PCM_UNLOCK(d);
541
542 bzero(fmtstr, sizeof(fmtstr));
543
544 if (snd_afmt2str(*vchanformat, fmtstr, sizeof(fmtstr)) != *vchanformat)
545 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr));
546
547 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
548 if (ret != 0 || req->newptr == NULL) {
549 PCM_RELEASE_QUICK(d);
550 return (ret);
551 }
552
553 newfmt = snd_str2afmt(fmtstr);
554 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) {
555 PCM_RELEASE_QUICK(d);
556 return (EINVAL);
557 }
558
559 CHN_FOREACH(c, d, channels.pcm.primary) {
560 CHN_LOCK(c);
561 if (c->direction != direction) {
562 CHN_UNLOCK(c);
563 continue;
564 }
565 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) {
566 if (CHN_STARTED(c)) {
567 chn_abort(c);
568 restart = 1;
569 } else
570 restart = 0;
571
572 ret = chn_reset(c, newfmt, c->speed);
573 if (ret == 0) {
574 if (restart != 0) {
575 CHN_FOREACH(ch, c, children.busy) {
576 CHN_LOCK(ch);
577 if (VCHAN_SYNC_REQUIRED(ch))
578 vchan_sync(ch);
579 CHN_UNLOCK(ch);
580 }
581 c->flags |= CHN_F_DIRTY;
582 ret = chn_start(c, 1);
583 }
584 }
585 }
586 *vchanformat = c->format;
587
588 CHN_UNLOCK(c);
589 }
590
591 PCM_RELEASE_QUICK(d);
592
593 return (ret);
594 }
595
596 /* virtual channel interface */
597
598 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
599 "play.vchanformat" : "rec.vchanformat"
600 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
601 "play.vchanrate" : "rec.vchanrate"
602
603 int
vchan_create(struct pcm_channel * parent,struct pcm_channel ** child)604 vchan_create(struct pcm_channel *parent, struct pcm_channel **child)
605 {
606 struct snddev_info *d;
607 struct pcm_channel *ch;
608 struct pcmchan_caps *parent_caps;
609 uint32_t vchanfmt, vchanspd;
610 int ret, direction, r;
611 bool save;
612
613 ret = 0;
614 save = false;
615 d = parent->parentsnddev;
616
617 PCM_BUSYASSERT(d);
618 CHN_LOCKASSERT(parent);
619
620 if (!(parent->direction == PCMDIR_PLAY ||
621 parent->direction == PCMDIR_REC))
622 return (EINVAL);
623
624 CHN_UNLOCK(parent);
625 PCM_LOCK(d);
626
627 if (parent->direction == PCMDIR_PLAY) {
628 direction = PCMDIR_PLAY_VIRTUAL;
629 vchanfmt = d->pvchanformat;
630 vchanspd = d->pvchanrate;
631 } else {
632 direction = PCMDIR_REC_VIRTUAL;
633 vchanfmt = d->rvchanformat;
634 vchanspd = d->rvchanrate;
635 }
636
637 /* create a new playback channel */
638 ch = chn_init(d, parent, &vchan_class, direction, parent);
639 if (ch == NULL) {
640 PCM_UNLOCK(d);
641 CHN_LOCK(parent);
642 return (ENODEV);
643 }
644 PCM_UNLOCK(d);
645
646 CHN_LOCK(parent);
647 CHN_INSERT_SORT_ASCEND(parent, ch, children);
648
649 *child = ch;
650
651 if (parent->flags & CHN_F_HAS_VCHAN)
652 return (0);
653
654 parent->flags |= CHN_F_HAS_VCHAN | CHN_F_BUSY;
655
656 parent_caps = chn_getcaps(parent);
657 if (parent_caps == NULL) {
658 ret = EINVAL;
659 goto fail;
660 }
661
662 if (vchanfmt == 0) {
663 const char *vfmt;
664
665 CHN_UNLOCK(parent);
666 r = resource_string_value(device_get_name(parent->dev),
667 device_get_unit(parent->dev), VCHAN_FMT_HINT(direction),
668 &vfmt);
669 CHN_LOCK(parent);
670 if (r != 0)
671 vfmt = NULL;
672 if (vfmt != NULL) {
673 vchanfmt = snd_str2afmt(vfmt);
674 if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN))
675 vchanfmt = 0;
676 }
677 if (vchanfmt == 0)
678 vchanfmt = VCHAN_DEFAULT_FORMAT;
679 save = true;
680 }
681
682 if (vchanspd == 0) {
683 /*
684 * This is very sad. Few soundcards advertised as being
685 * able to do (insanely) higher/lower speed, but in
686 * reality, they simply can't. At least, we give user chance
687 * to set sane value via kernel hints or sysctl.
688 */
689 CHN_UNLOCK(parent);
690 r = resource_int_value(device_get_name(parent->dev),
691 device_get_unit(parent->dev), VCHAN_SPD_HINT(direction),
692 &vchanspd);
693 CHN_LOCK(parent);
694 if (r != 0) {
695 /* No saved value, no hint, NOTHING. */
696 vchanspd = VCHAN_DEFAULT_RATE;
697 RANGE(vchanspd, parent_caps->minspeed,
698 parent_caps->maxspeed);
699 }
700 save = true;
701 }
702
703 /*
704 * Limit the speed between feeder_rate_min <-> feeder_rate_max.
705 */
706 RANGE(vchanspd, feeder_rate_min, feeder_rate_max);
707
708 if (feeder_rate_round) {
709 RANGE(vchanspd, parent_caps->minspeed,
710 parent_caps->maxspeed);
711 vchanspd = CHANNEL_SETSPEED(parent->methods,
712 parent->devinfo, vchanspd);
713 }
714
715 if ((ret = chn_reset(parent, vchanfmt, vchanspd)) != 0)
716 goto fail;
717
718 if (save) {
719 /*
720 * Save new value.
721 */
722 if (direction == PCMDIR_PLAY_VIRTUAL) {
723 d->pvchanformat = parent->format;
724 d->pvchanrate = parent->speed;
725 } else {
726 d->rvchanformat = parent->format;
727 d->rvchanrate = parent->speed;
728 }
729 }
730
731 /*
732 * If the parent channel supports digital format,
733 * enable passthrough mode.
734 */
735 if (snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
736 parent->flags &= ~CHN_F_VCHAN_DYNAMIC;
737 parent->flags |= CHN_F_VCHAN_PASSTHROUGH;
738 }
739
740 return (ret);
741
742 fail:
743 CHN_LOCK(ch);
744 vchan_destroy(ch);
745 *child = NULL;
746
747 return (ret);
748 }
749
750 int
vchan_destroy(struct pcm_channel * c)751 vchan_destroy(struct pcm_channel *c)
752 {
753 struct pcm_channel *parent;
754
755 KASSERT(c != NULL && c->parentchannel != NULL &&
756 c->parentsnddev != NULL, ("%s(): invalid channel=%p",
757 __func__, c));
758
759 CHN_LOCKASSERT(c);
760
761 parent = c->parentchannel;
762
763 PCM_BUSYASSERT(c->parentsnddev);
764 CHN_LOCKASSERT(parent);
765
766 CHN_UNLOCK(c);
767
768 /* remove us from our parent's children list */
769 CHN_REMOVE(parent, c, children);
770 if (CHN_EMPTY(parent, children)) {
771 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
772 chn_reset(parent, parent->format, parent->speed);
773 }
774
775 CHN_UNLOCK(parent);
776
777 /* destroy ourselves */
778 chn_kill(c);
779
780 CHN_LOCK(parent);
781
782 return (0);
783 }
784
785 int
786 #ifdef SND_DEBUG
vchan_passthrough(struct pcm_channel * c,const char * caller)787 vchan_passthrough(struct pcm_channel *c, const char *caller)
788 #else
789 vchan_sync(struct pcm_channel *c)
790 #endif
791 {
792 int ret;
793
794 KASSERT(c != NULL && c->parentchannel != NULL &&
795 (c->flags & CHN_F_VIRTUAL),
796 ("%s(): invalid passthrough", __func__));
797 CHN_LOCKASSERT(c);
798 CHN_LOCKASSERT(c->parentchannel);
799
800 sndbuf_setspd(c->bufhard, c->parentchannel->speed);
801 c->flags |= CHN_F_PASSTHROUGH;
802 ret = feeder_chain(c);
803 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH);
804 if (ret != 0)
805 c->flags |= CHN_F_DIRTY;
806
807 #ifdef SND_DEBUG
808 if (snd_passthrough_verbose) {
809 device_printf(c->dev, "%s(%s/%s) %s() -> re-sync err=%d\n",
810 __func__, c->name, c->comm, caller, ret);
811 }
812 #endif
813
814 return (ret);
815 }
816
817 static int
sysctl_hw_snd_vchans_enable(SYSCTL_HANDLER_ARGS)818 sysctl_hw_snd_vchans_enable(SYSCTL_HANDLER_ARGS)
819 {
820 struct snddev_info *d;
821 int i, v, error;
822
823 v = snd_vchans_enable;
824 error = sysctl_handle_int(oidp, &v, 0, req);
825 if (error != 0 || req->newptr == NULL)
826 return (error);
827
828 snd_vchans_enable = v >= 1;
829
830 for (i = 0; pcm_devclass != NULL &&
831 i < devclass_get_maxunit(pcm_devclass); i++) {
832 d = devclass_get_softc(pcm_devclass, i);
833 if (!PCM_REGISTERED(d))
834 continue;
835 PCM_ACQUIRE_QUICK(d);
836 if (snd_vchans_enable) {
837 if (d->playcount > 0)
838 d->flags |= SD_F_PVCHANS;
839 if (d->reccount > 0)
840 d->flags |= SD_F_RVCHANS;
841 } else
842 d->flags &= ~(SD_F_PVCHANS | SD_F_RVCHANS);
843 PCM_RELEASE_QUICK(d);
844 }
845
846 return (0);
847 }
848 SYSCTL_PROC(_hw_snd, OID_AUTO, vchans_enable,
849 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
850 sysctl_hw_snd_vchans_enable, "I", "global virtual channel switch");
851
852 void
vchan_initsys(device_t dev)853 vchan_initsys(device_t dev)
854 {
855 struct snddev_info *d;
856 int unit;
857
858 unit = device_get_unit(dev);
859 d = device_get_softc(dev);
860
861 /* Play */
862 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
863 SYSCTL_CHILDREN(d->play_sysctl_tree),
864 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
865 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
866 sysctl_dev_pcm_vchans, "I", "virtual channels enabled");
867 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
868 SYSCTL_CHILDREN(d->play_sysctl_tree),
869 OID_AUTO, "vchanmode",
870 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
871 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
872 sysctl_dev_pcm_vchanmode, "A",
873 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
874 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
875 SYSCTL_CHILDREN(d->play_sysctl_tree),
876 OID_AUTO, "vchanrate",
877 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
878 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
879 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
880 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
881 SYSCTL_CHILDREN(d->play_sysctl_tree),
882 OID_AUTO, "vchanformat",
883 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
884 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
885 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
886 /* Rec */
887 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
888 SYSCTL_CHILDREN(d->rec_sysctl_tree),
889 OID_AUTO, "vchans",
890 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
891 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
892 sysctl_dev_pcm_vchans, "I", "virtual channels enabled");
893 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
894 SYSCTL_CHILDREN(d->rec_sysctl_tree),
895 OID_AUTO, "vchanmode",
896 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
897 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
898 sysctl_dev_pcm_vchanmode, "A",
899 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
900 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
901 SYSCTL_CHILDREN(d->rec_sysctl_tree),
902 OID_AUTO, "vchanrate",
903 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
904 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
905 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
906 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
907 SYSCTL_CHILDREN(d->rec_sysctl_tree),
908 OID_AUTO, "vchanformat",
909 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
910 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
911 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
912 }
913