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 bus_topo_lock();
263 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
264 if (!PCM_REGISTERED(d)) {
265 bus_topo_unlock();
266 return (EINVAL);
267 }
268 bus_topo_unlock();
269
270 PCM_LOCK(d);
271 PCM_WAIT(d);
272
273 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
274 case VCHAN_PLAY:
275 /* Exit if we do not support this direction. */
276 if (d->playcount < 1) {
277 PCM_UNLOCK(d);
278 return (ENODEV);
279 }
280 flag = SD_F_PVCHANS;
281 break;
282 case VCHAN_REC:
283 if (d->reccount < 1) {
284 PCM_UNLOCK(d);
285 return (ENODEV);
286 }
287 flag = SD_F_RVCHANS;
288 break;
289 default:
290 PCM_UNLOCK(d);
291 return (EINVAL);
292 }
293
294 enabled = (d->flags & flag) != 0;
295
296 PCM_ACQUIRE(d);
297 PCM_UNLOCK(d);
298
299 err = sysctl_handle_int(oidp, &enabled, 0, req);
300 if (err != 0 || req->newptr == NULL) {
301 PCM_RELEASE_QUICK(d);
302 return (err);
303 }
304
305 if (enabled <= 0)
306 d->flags &= ~flag;
307 else
308 d->flags |= flag;
309
310 PCM_RELEASE_QUICK(d);
311
312 return (0);
313 }
314
315 static int
sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)316 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
317 {
318 struct snddev_info *d;
319 struct pcm_channel *c;
320 uint32_t dflags;
321 int *vchanmode, direction, ret;
322 char dtype[16];
323
324 bus_topo_lock();
325 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
326 if (!PCM_REGISTERED(d)) {
327 bus_topo_unlock();
328 return (EINVAL);
329 }
330 bus_topo_unlock();
331
332 PCM_LOCK(d);
333 PCM_WAIT(d);
334
335 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
336 case VCHAN_PLAY:
337 if ((d->flags & SD_F_PVCHANS) == 0) {
338 PCM_UNLOCK(d);
339 return (ENODEV);
340 }
341 direction = PCMDIR_PLAY;
342 vchanmode = &d->pvchanmode;
343 break;
344 case VCHAN_REC:
345 if ((d->flags & SD_F_RVCHANS) == 0) {
346 PCM_UNLOCK(d);
347 return (ENODEV);
348 }
349 direction = PCMDIR_REC;
350 vchanmode = &d->rvchanmode;
351 break;
352 default:
353 PCM_UNLOCK(d);
354 return (EINVAL);
355 }
356
357 PCM_ACQUIRE(d);
358 PCM_UNLOCK(d);
359
360 if (*vchanmode & CHN_F_VCHAN_PASSTHROUGH)
361 strlcpy(dtype, "passthrough", sizeof(dtype));
362 else if (*vchanmode & CHN_F_VCHAN_ADAPTIVE)
363 strlcpy(dtype, "adaptive", sizeof(dtype));
364 else
365 strlcpy(dtype, "fixed", sizeof(dtype));
366
367 ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req);
368 if (ret != 0 || req->newptr == NULL) {
369 PCM_RELEASE_QUICK(d);
370 return (ret);
371 }
372
373 if (strcasecmp(dtype, "passthrough") == 0 || strcmp(dtype, "1") == 0)
374 dflags = CHN_F_VCHAN_PASSTHROUGH;
375 else if (strcasecmp(dtype, "adaptive") == 0 || strcmp(dtype, "2") == 0)
376 dflags = CHN_F_VCHAN_ADAPTIVE;
377 else if (strcasecmp(dtype, "fixed") == 0 || strcmp(dtype, "0") == 0)
378 dflags = 0;
379 else {
380 PCM_RELEASE_QUICK(d);
381 return (EINVAL);
382 }
383
384 CHN_FOREACH(c, d, channels.pcm.primary) {
385 CHN_LOCK(c);
386 if (c->direction != direction ||
387 dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) ||
388 (c->flags & CHN_F_PASSTHROUGH)) {
389 CHN_UNLOCK(c);
390 continue;
391 }
392 c->flags &= ~CHN_F_VCHAN_DYNAMIC;
393 c->flags |= dflags;
394 CHN_UNLOCK(c);
395 *vchanmode = dflags;
396 }
397
398 PCM_RELEASE_QUICK(d);
399
400 return (ret);
401 }
402
403 /*
404 * On the fly vchan rate/format settings
405 */
406
407 #define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \
408 CHN_F_EXCLUSIVE)) && \
409 (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \
410 CHN_STOPPED(c)))
411 static int
sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)412 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
413 {
414 struct snddev_info *d;
415 struct pcm_channel *c, *ch;
416 int *vchanrate, direction, ret, newspd, restart;
417
418 bus_topo_lock();
419 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
420 if (!PCM_REGISTERED(d)) {
421 bus_topo_unlock();
422 return (EINVAL);
423 }
424 bus_topo_unlock();
425
426 PCM_LOCK(d);
427 PCM_WAIT(d);
428
429 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
430 case VCHAN_PLAY:
431 if ((d->flags & SD_F_PVCHANS) == 0) {
432 PCM_UNLOCK(d);
433 return (ENODEV);
434 }
435 direction = PCMDIR_PLAY;
436 vchanrate = &d->pvchanrate;
437 break;
438 case VCHAN_REC:
439 if ((d->flags & SD_F_RVCHANS) == 0) {
440 PCM_UNLOCK(d);
441 return (ENODEV);
442 }
443 direction = PCMDIR_REC;
444 vchanrate = &d->rvchanrate;
445 break;
446 default:
447 PCM_UNLOCK(d);
448 return (EINVAL);
449 }
450
451 PCM_ACQUIRE(d);
452 PCM_UNLOCK(d);
453
454 newspd = *vchanrate;
455
456 ret = sysctl_handle_int(oidp, &newspd, 0, req);
457 if (ret != 0 || req->newptr == NULL) {
458 PCM_RELEASE_QUICK(d);
459 return (ret);
460 }
461
462 if (newspd < feeder_rate_min || newspd > feeder_rate_max) {
463 PCM_RELEASE_QUICK(d);
464 return (EINVAL);
465 }
466
467 CHN_FOREACH(c, d, channels.pcm.primary) {
468 CHN_LOCK(c);
469 if (c->direction != direction) {
470 CHN_UNLOCK(c);
471 continue;
472 }
473
474 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) {
475 if (CHN_STARTED(c)) {
476 chn_abort(c);
477 restart = 1;
478 } else
479 restart = 0;
480
481 ret = chn_reset(c, c->format, newspd);
482 if (ret == 0) {
483 if (restart != 0) {
484 CHN_FOREACH(ch, c, children.busy) {
485 CHN_LOCK(ch);
486 if (VCHAN_SYNC_REQUIRED(ch))
487 vchan_sync(ch);
488 CHN_UNLOCK(ch);
489 }
490 c->flags |= CHN_F_DIRTY;
491 ret = chn_start(c, 1);
492 }
493 }
494 }
495 *vchanrate = sndbuf_getspd(c->bufsoft);
496
497 CHN_UNLOCK(c);
498 }
499
500 PCM_RELEASE_QUICK(d);
501
502 return (ret);
503 }
504
505 static int
sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)506 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
507 {
508 struct snddev_info *d;
509 struct pcm_channel *c, *ch;
510 uint32_t newfmt;
511 int *vchanformat, direction, ret, restart;
512 char fmtstr[AFMTSTR_LEN];
513
514 bus_topo_lock();
515 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
516 if (!PCM_REGISTERED(d)) {
517 bus_topo_unlock();
518 return (EINVAL);
519 }
520 bus_topo_unlock();
521
522 PCM_LOCK(d);
523 PCM_WAIT(d);
524
525 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
526 case VCHAN_PLAY:
527 if ((d->flags & SD_F_PVCHANS) == 0) {
528 PCM_UNLOCK(d);
529 return (ENODEV);
530 }
531 direction = PCMDIR_PLAY;
532 vchanformat = &d->pvchanformat;
533 break;
534 case VCHAN_REC:
535 if ((d->flags & SD_F_RVCHANS) == 0) {
536 PCM_UNLOCK(d);
537 return (ENODEV);
538 }
539 direction = PCMDIR_REC;
540 vchanformat = &d->rvchanformat;
541 break;
542 default:
543 PCM_UNLOCK(d);
544 return (EINVAL);
545 }
546
547 PCM_ACQUIRE(d);
548 PCM_UNLOCK(d);
549
550 bzero(fmtstr, sizeof(fmtstr));
551
552 if (snd_afmt2str(*vchanformat, fmtstr, sizeof(fmtstr)) != *vchanformat)
553 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr));
554
555 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
556 if (ret != 0 || req->newptr == NULL) {
557 PCM_RELEASE_QUICK(d);
558 return (ret);
559 }
560
561 newfmt = snd_str2afmt(fmtstr);
562 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) {
563 PCM_RELEASE_QUICK(d);
564 return (EINVAL);
565 }
566
567 CHN_FOREACH(c, d, channels.pcm.primary) {
568 CHN_LOCK(c);
569 if (c->direction != direction) {
570 CHN_UNLOCK(c);
571 continue;
572 }
573 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) {
574 if (CHN_STARTED(c)) {
575 chn_abort(c);
576 restart = 1;
577 } else
578 restart = 0;
579
580 ret = chn_reset(c, newfmt, c->speed);
581 if (ret == 0) {
582 if (restart != 0) {
583 CHN_FOREACH(ch, c, children.busy) {
584 CHN_LOCK(ch);
585 if (VCHAN_SYNC_REQUIRED(ch))
586 vchan_sync(ch);
587 CHN_UNLOCK(ch);
588 }
589 c->flags |= CHN_F_DIRTY;
590 ret = chn_start(c, 1);
591 }
592 }
593 }
594 *vchanformat = sndbuf_getfmt(c->bufsoft);
595
596 CHN_UNLOCK(c);
597 }
598
599 PCM_RELEASE_QUICK(d);
600
601 return (ret);
602 }
603
604 /* virtual channel interface */
605
606 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
607 "play.vchanformat" : "rec.vchanformat"
608 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
609 "play.vchanrate" : "rec.vchanrate"
610
611 int
vchan_create(struct pcm_channel * parent,struct pcm_channel ** child)612 vchan_create(struct pcm_channel *parent, struct pcm_channel **child)
613 {
614 struct snddev_info *d;
615 struct pcm_channel *ch;
616 struct pcmchan_caps *parent_caps;
617 uint32_t vchanfmt, vchanspd;
618 int ret, direction;
619
620 ret = 0;
621 d = parent->parentsnddev;
622
623 PCM_BUSYASSERT(d);
624 CHN_LOCKASSERT(parent);
625
626 if (!(parent->direction == PCMDIR_PLAY ||
627 parent->direction == PCMDIR_REC))
628 return (EINVAL);
629
630 CHN_UNLOCK(parent);
631 PCM_LOCK(d);
632
633 if (parent->direction == PCMDIR_PLAY) {
634 direction = PCMDIR_PLAY_VIRTUAL;
635 vchanfmt = d->pvchanformat;
636 vchanspd = d->pvchanrate;
637 } else {
638 direction = PCMDIR_REC_VIRTUAL;
639 vchanfmt = d->rvchanformat;
640 vchanspd = d->rvchanrate;
641 }
642
643 /* create a new playback channel */
644 ch = chn_init(d, parent, &vchan_class, direction, parent);
645 if (ch == NULL) {
646 PCM_UNLOCK(d);
647 CHN_LOCK(parent);
648 return (ENODEV);
649 }
650 PCM_UNLOCK(d);
651
652 CHN_LOCK(parent);
653 CHN_INSERT_SORT_ASCEND(parent, ch, children);
654
655 *child = ch;
656
657 if (parent->flags & CHN_F_HAS_VCHAN)
658 return (0);
659
660 parent->flags |= CHN_F_HAS_VCHAN | CHN_F_BUSY;
661
662 parent_caps = chn_getcaps(parent);
663 if (parent_caps == NULL) {
664 ret = EINVAL;
665 goto fail;
666 }
667
668 if ((ret = chn_reset(parent, vchanfmt, vchanspd)) != 0)
669 goto fail;
670
671 /*
672 * If the parent channel supports digital format,
673 * enable passthrough mode.
674 */
675 if (snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
676 parent->flags &= ~CHN_F_VCHAN_DYNAMIC;
677 parent->flags |= CHN_F_VCHAN_PASSTHROUGH;
678 }
679
680 return (ret);
681
682 fail:
683 CHN_LOCK(ch);
684 vchan_destroy(ch);
685 *child = NULL;
686
687 return (ret);
688 }
689
690 int
vchan_destroy(struct pcm_channel * c)691 vchan_destroy(struct pcm_channel *c)
692 {
693 struct pcm_channel *parent;
694
695 KASSERT(c != NULL && c->parentchannel != NULL &&
696 c->parentsnddev != NULL, ("%s(): invalid channel=%p",
697 __func__, c));
698
699 CHN_LOCKASSERT(c);
700
701 parent = c->parentchannel;
702
703 PCM_BUSYASSERT(c->parentsnddev);
704 CHN_LOCKASSERT(parent);
705
706 CHN_UNLOCK(c);
707
708 /* remove us from our parent's children list */
709 CHN_REMOVE(parent, c, children);
710 if (CHN_EMPTY(parent, children)) {
711 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
712 chn_reset(parent, parent->format, parent->speed);
713 }
714
715 CHN_UNLOCK(parent);
716
717 /* destroy ourselves */
718 chn_kill(c);
719
720 CHN_LOCK(parent);
721
722 return (0);
723 }
724
725 int
726 #ifdef SND_DEBUG
vchan_passthrough(struct pcm_channel * c,const char * caller)727 vchan_passthrough(struct pcm_channel *c, const char *caller)
728 #else
729 vchan_sync(struct pcm_channel *c)
730 #endif
731 {
732 int ret;
733
734 KASSERT(c != NULL && c->parentchannel != NULL &&
735 (c->flags & CHN_F_VIRTUAL),
736 ("%s(): invalid passthrough", __func__));
737 CHN_LOCKASSERT(c);
738 CHN_LOCKASSERT(c->parentchannel);
739
740 sndbuf_setspd(c->bufhard, c->parentchannel->speed);
741 c->flags |= CHN_F_PASSTHROUGH;
742 ret = feeder_chain(c);
743 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH);
744 if (ret != 0)
745 c->flags |= CHN_F_DIRTY;
746
747 #ifdef SND_DEBUG
748 if (snd_passthrough_verbose) {
749 device_printf(c->dev, "%s(%s/%s) %s() -> re-sync err=%d\n",
750 __func__, c->name, c->comm, caller, ret);
751 }
752 #endif
753
754 return (ret);
755 }
756
757 static int
sysctl_hw_snd_vchans_enable(SYSCTL_HANDLER_ARGS)758 sysctl_hw_snd_vchans_enable(SYSCTL_HANDLER_ARGS)
759 {
760 struct snddev_info *d;
761 int i, v, error;
762
763 v = snd_vchans_enable;
764 error = sysctl_handle_int(oidp, &v, 0, req);
765 if (error != 0 || req->newptr == NULL)
766 return (error);
767
768 bus_topo_lock();
769 snd_vchans_enable = v >= 1;
770
771 for (i = 0; pcm_devclass != NULL &&
772 i < devclass_get_maxunit(pcm_devclass); i++) {
773 d = devclass_get_softc(pcm_devclass, i);
774 if (!PCM_REGISTERED(d))
775 continue;
776 PCM_ACQUIRE_QUICK(d);
777 if (snd_vchans_enable) {
778 if (d->playcount > 0)
779 d->flags |= SD_F_PVCHANS;
780 if (d->reccount > 0)
781 d->flags |= SD_F_RVCHANS;
782 } else
783 d->flags &= ~(SD_F_PVCHANS | SD_F_RVCHANS);
784 PCM_RELEASE_QUICK(d);
785 }
786 bus_topo_unlock();
787
788 return (0);
789 }
790 SYSCTL_PROC(_hw_snd, OID_AUTO, vchans_enable,
791 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
792 sysctl_hw_snd_vchans_enable, "I", "global virtual channel switch");
793
794 void
vchan_initsys(device_t dev)795 vchan_initsys(device_t dev)
796 {
797 struct snddev_info *d;
798 int unit;
799
800 unit = device_get_unit(dev);
801 d = device_get_softc(dev);
802
803 /* Play */
804 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
805 SYSCTL_CHILDREN(d->play_sysctl_tree),
806 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
807 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
808 sysctl_dev_pcm_vchans, "I", "virtual channels enabled");
809 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
810 SYSCTL_CHILDREN(d->play_sysctl_tree),
811 OID_AUTO, "vchanmode",
812 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
813 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
814 sysctl_dev_pcm_vchanmode, "A",
815 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
816 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
817 SYSCTL_CHILDREN(d->play_sysctl_tree),
818 OID_AUTO, "vchanrate",
819 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
820 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
821 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
822 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
823 SYSCTL_CHILDREN(d->play_sysctl_tree),
824 OID_AUTO, "vchanformat",
825 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
826 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
827 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
828 /* Rec */
829 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
830 SYSCTL_CHILDREN(d->rec_sysctl_tree),
831 OID_AUTO, "vchans",
832 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
833 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
834 sysctl_dev_pcm_vchans, "I", "virtual channels enabled");
835 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
836 SYSCTL_CHILDREN(d->rec_sysctl_tree),
837 OID_AUTO, "vchanmode",
838 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
839 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
840 sysctl_dev_pcm_vchanmode, "A",
841 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
842 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
843 SYSCTL_CHILDREN(d->rec_sysctl_tree),
844 OID_AUTO, "vchanrate",
845 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
846 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
847 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
848 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
849 SYSCTL_CHILDREN(d->rec_sysctl_tree),
850 OID_AUTO, "vchanformat",
851 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
852 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
853 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
854 }
855