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
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 int snd_maxautovchans = 16;
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
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
191 static struct pcmchan_caps *
vchan_getcaps(kobj_t obj,void * data)192 vchan_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
238 static struct pcmchan_matrix *
vchan_getmatrix(kobj_t obj,void * data,uint32_t format)239 vchan_getmatrix(kobj_t obj, void *data, uint32_t format)
240 {
241
242 return (feeder_matrix_format_map(format));
243 }
244
245 static 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 };
255 CHANNEL_DECLARE(vchan);
256
257 static void
vchan_getparentchannel(struct snddev_info * d,struct pcm_channel ** wrch,struct pcm_channel ** rdch)258 vchan_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
299 static int
sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)300 sysctl_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
354 static int
sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)355 sysctl_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)))
448 static int
sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)449 sysctl_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 < feeder_rate_min || newspd > feeder_rate_max) {
512 PCM_RELEASE_QUICK(d);
513 return (EINVAL);
514 }
515
516 CHN_LOCK(c);
517
518 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) {
519 if (CHN_STARTED(c)) {
520 chn_abort(c);
521 restart = 1;
522 } else
523 restart = 0;
524
525 if (feeder_rate_round) {
526 caps = chn_getcaps(c);
527 RANGE(newspd, caps->minspeed, caps->maxspeed);
528 newspd = CHANNEL_SETSPEED(c->methods,
529 c->devinfo, newspd);
530 }
531
532 ret = chn_reset(c, c->format, newspd);
533 if (ret == 0) {
534 *vchanrate = c->speed;
535 if (restart != 0) {
536 CHN_FOREACH(ch, c, children.busy) {
537 CHN_LOCK(ch);
538 if (VCHAN_SYNC_REQUIRED(ch))
539 vchan_sync(ch);
540 CHN_UNLOCK(ch);
541 }
542 c->flags |= CHN_F_DIRTY;
543 ret = chn_start(c, 1);
544 }
545 }
546 }
547
548 CHN_UNLOCK(c);
549
550 PCM_RELEASE_QUICK(d);
551
552 return (ret);
553 }
554
555 static int
sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)556 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
557 {
558 struct snddev_info *d;
559 struct pcm_channel *c, *ch;
560 uint32_t newfmt;
561 int *vchanformat, vchancount, direction, ret, restart;
562 char fmtstr[AFMTSTR_LEN];
563
564 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
565 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
566 return (EINVAL);
567
568 PCM_LOCK(d);
569 PCM_WAIT(d);
570
571 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
572 case VCHAN_PLAY:
573 direction = PCMDIR_PLAY;
574 vchancount = d->pvchancount;
575 vchanformat = &d->pvchanformat;
576 break;
577 case VCHAN_REC:
578 direction = PCMDIR_REC;
579 vchancount = d->rvchancount;
580 vchanformat = &d->rvchanformat;
581 break;
582 default:
583 PCM_UNLOCK(d);
584 return (EINVAL);
585 break;
586 }
587
588 if (vchancount < 1) {
589 PCM_UNLOCK(d);
590 return (EINVAL);
591 }
592
593 PCM_ACQUIRE(d);
594 PCM_UNLOCK(d);
595
596 if (direction == PCMDIR_PLAY)
597 vchan_getparentchannel(d, &c, NULL);
598 else
599 vchan_getparentchannel(d, NULL, &c);
600
601 if (c == NULL) {
602 PCM_RELEASE_QUICK(d);
603 return (EINVAL);
604 }
605
606 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
607 __func__, direction, c->direction));
608
609 CHN_LOCK(c);
610
611 bzero(fmtstr, sizeof(fmtstr));
612
613 if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format)
614 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr));
615
616 CHN_UNLOCK(c);
617
618 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
619 if (ret != 0 || req->newptr == NULL) {
620 PCM_RELEASE_QUICK(d);
621 return (ret);
622 }
623
624 newfmt = snd_str2afmt(fmtstr);
625 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) {
626 PCM_RELEASE_QUICK(d);
627 return (EINVAL);
628 }
629
630 CHN_LOCK(c);
631
632 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) {
633 if (CHN_STARTED(c)) {
634 chn_abort(c);
635 restart = 1;
636 } else
637 restart = 0;
638
639 ret = chn_reset(c, newfmt, c->speed);
640 if (ret == 0) {
641 *vchanformat = c->format;
642 if (restart != 0) {
643 CHN_FOREACH(ch, c, children.busy) {
644 CHN_LOCK(ch);
645 if (VCHAN_SYNC_REQUIRED(ch))
646 vchan_sync(ch);
647 CHN_UNLOCK(ch);
648 }
649 c->flags |= CHN_F_DIRTY;
650 ret = chn_start(c, 1);
651 }
652 }
653 }
654
655 CHN_UNLOCK(c);
656
657 PCM_RELEASE_QUICK(d);
658
659 return (ret);
660 }
661
662 /* virtual channel interface */
663
664 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
665 "play.vchanformat" : "rec.vchanformat"
666 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
667 "play.vchanrate" : "rec.vchanrate"
668
669 int
vchan_create(struct pcm_channel * parent)670 vchan_create(struct pcm_channel *parent)
671 {
672 struct snddev_info *d;
673 struct pcm_channel *ch;
674 struct pcmchan_caps *parent_caps;
675 uint32_t vchanfmt, vchanspd;
676 int ret, direction, r;
677 bool save;
678
679 ret = 0;
680 save = false;
681 d = parent->parentsnddev;
682
683 PCM_BUSYASSERT(d);
684 CHN_LOCKASSERT(parent);
685
686 if (!(parent->flags & CHN_F_BUSY))
687 return (EBUSY);
688
689 if (!(parent->direction == PCMDIR_PLAY ||
690 parent->direction == PCMDIR_REC))
691 return (EINVAL);
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 PCM_UNLOCK(d);
714
715 CHN_LOCK(parent);
716 /*
717 * Add us to our parent channel's children in reverse order
718 * so future destruction will pick the last (biggest number)
719 * channel.
720 */
721 CHN_INSERT_SORT_DESCEND(parent, ch, children);
722
723 if (parent->flags & CHN_F_HAS_VCHAN)
724 return (0);
725
726 parent->flags |= CHN_F_HAS_VCHAN;
727
728 parent_caps = chn_getcaps(parent);
729 if (parent_caps == NULL) {
730 ret = EINVAL;
731 goto fail;
732 }
733
734 if (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 = true;
752 }
753
754 if (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 /* No saved value, no hint, NOTHING. */
768 vchanspd = VCHAN_DEFAULT_RATE;
769 RANGE(vchanspd, parent_caps->minspeed,
770 parent_caps->maxspeed);
771 }
772 save = true;
773 }
774
775 /*
776 * Limit the speed between feeder_rate_min <-> feeder_rate_max.
777 */
778 RANGE(vchanspd, feeder_rate_min, feeder_rate_max);
779
780 if (feeder_rate_round) {
781 RANGE(vchanspd, parent_caps->minspeed,
782 parent_caps->maxspeed);
783 vchanspd = CHANNEL_SETSPEED(parent->methods,
784 parent->devinfo, vchanspd);
785 }
786
787 if ((ret = chn_reset(parent, vchanfmt, vchanspd)) != 0)
788 goto fail;
789
790 if (save) {
791 /*
792 * Save new value.
793 */
794 if (direction == PCMDIR_PLAY_VIRTUAL) {
795 d->pvchanformat = parent->format;
796 d->pvchanrate = parent->speed;
797 } else {
798 d->rvchanformat = parent->format;
799 d->rvchanrate = parent->speed;
800 }
801 }
802
803 /*
804 * If the parent channel supports digital format,
805 * enable passthrough mode.
806 */
807 if (snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
808 parent->flags &= ~CHN_F_VCHAN_DYNAMIC;
809 parent->flags |= CHN_F_VCHAN_PASSTHROUGH;
810 }
811
812 return (ret);
813
814 fail:
815 CHN_REMOVE(parent, ch, children);
816 parent->flags &= ~CHN_F_HAS_VCHAN;
817 CHN_UNLOCK(parent);
818 chn_kill(ch);
819 CHN_LOCK(parent);
820
821 return (ret);
822 }
823
824 int
vchan_destroy(struct pcm_channel * c)825 vchan_destroy(struct pcm_channel *c)
826 {
827 struct pcm_channel *parent;
828
829 KASSERT(c != NULL && c->parentchannel != NULL &&
830 c->parentsnddev != NULL, ("%s(): invalid channel=%p",
831 __func__, c));
832
833 CHN_LOCKASSERT(c);
834
835 parent = c->parentchannel;
836
837 PCM_BUSYASSERT(c->parentsnddev);
838 CHN_LOCKASSERT(parent);
839
840 CHN_UNLOCK(c);
841
842 if (!(parent->flags & CHN_F_BUSY))
843 return (EBUSY);
844
845 if (CHN_EMPTY(parent, children))
846 return (EINVAL);
847
848 /* remove us from our parent's children list */
849 CHN_REMOVE(parent, c, children);
850
851 if (CHN_EMPTY(parent, children)) {
852 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
853 chn_reset(parent, parent->format, parent->speed);
854 }
855
856 CHN_UNLOCK(parent);
857
858 /* destroy ourselves */
859 chn_kill(c);
860
861 CHN_LOCK(parent);
862
863 return (0);
864 }
865
866 int
867 #ifdef SND_DEBUG
vchan_passthrough(struct pcm_channel * c,const char * caller)868 vchan_passthrough(struct pcm_channel *c, const char *caller)
869 #else
870 vchan_sync(struct pcm_channel *c)
871 #endif
872 {
873 int ret;
874
875 KASSERT(c != NULL && c->parentchannel != NULL &&
876 (c->flags & CHN_F_VIRTUAL),
877 ("%s(): invalid passthrough", __func__));
878 CHN_LOCKASSERT(c);
879 CHN_LOCKASSERT(c->parentchannel);
880
881 sndbuf_setspd(c->bufhard, c->parentchannel->speed);
882 c->flags |= CHN_F_PASSTHROUGH;
883 ret = feeder_chain(c);
884 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH);
885 if (ret != 0)
886 c->flags |= CHN_F_DIRTY;
887
888 #ifdef SND_DEBUG
889 if (snd_passthrough_verbose) {
890 device_printf(c->dev, "%s(%s/%s) %s() -> re-sync err=%d\n",
891 __func__, c->name, c->comm, caller, ret);
892 }
893 #endif
894
895 return (ret);
896 }
897
898 int
vchan_setnew(struct snddev_info * d,int direction,int newcnt)899 vchan_setnew(struct snddev_info *d, int direction, int newcnt)
900 {
901 struct pcm_channel *c, *ch, *nch;
902 struct pcmchan_caps *caps;
903 int i, err, vcnt;
904
905 PCM_BUSYASSERT(d);
906
907 if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
908 (direction == PCMDIR_REC && d->reccount < 1))
909 return (ENODEV);
910
911 if (!(d->flags & SD_F_AUTOVCHAN))
912 return (EINVAL);
913
914 if (newcnt < 0 || newcnt > SND_MAXVCHANS)
915 return (E2BIG);
916
917 if (direction == PCMDIR_PLAY)
918 vcnt = d->pvchancount;
919 else if (direction == PCMDIR_REC)
920 vcnt = d->rvchancount;
921 else
922 return (EINVAL);
923
924 if (newcnt > vcnt) {
925 /* add new vchans - find a parent channel first */
926 ch = NULL;
927 CHN_FOREACH(c, d, channels.pcm) {
928 CHN_LOCK(c);
929 if (c->direction == direction &&
930 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
931 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
932 /*
933 * Reuse hw channel with vchans already
934 * created.
935 */
936 if (c->flags & CHN_F_HAS_VCHAN) {
937 ch = c;
938 break;
939 }
940 /*
941 * No vchans ever created, look for
942 * channels with supported formats.
943 */
944 caps = chn_getcaps(c);
945 if (caps == NULL) {
946 CHN_UNLOCK(c);
947 continue;
948 }
949 for (i = 0; caps->fmtlist[i] != 0; i++) {
950 if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
951 break;
952 }
953 if (caps->fmtlist[i] != 0) {
954 ch = c;
955 break;
956 }
957 }
958 CHN_UNLOCK(c);
959 }
960 if (ch == NULL)
961 return (EBUSY);
962 ch->flags |= CHN_F_BUSY;
963 err = 0;
964 while (err == 0 && newcnt > vcnt) {
965 err = vchan_create(ch);
966 if (err == 0)
967 vcnt++;
968 else if (err == E2BIG && newcnt > vcnt)
969 device_printf(d->dev,
970 "%s: err=%d Maximum channel reached.\n",
971 __func__, err);
972 }
973 if (vcnt == 0)
974 ch->flags &= ~CHN_F_BUSY;
975 CHN_UNLOCK(ch);
976 if (err != 0)
977 return (err);
978 } else if (newcnt < vcnt) {
979 CHN_FOREACH(c, d, channels.pcm) {
980 CHN_LOCK(c);
981 if (c->direction != direction ||
982 CHN_EMPTY(c, children) ||
983 !(c->flags & CHN_F_HAS_VCHAN)) {
984 CHN_UNLOCK(c);
985 continue;
986 }
987 CHN_FOREACH_SAFE(ch, c, nch, children) {
988 CHN_LOCK(ch);
989 if (vcnt == 1 && ch->flags & CHN_F_BUSY) {
990 CHN_UNLOCK(ch);
991 break;
992 }
993 if (!(ch->flags & CHN_F_BUSY)) {
994 err = vchan_destroy(ch);
995 if (err == 0)
996 vcnt--;
997 } else
998 CHN_UNLOCK(ch);
999 if (vcnt == newcnt)
1000 break;
1001 }
1002 CHN_UNLOCK(c);
1003 break;
1004 }
1005 }
1006
1007 return (0);
1008 }
1009
1010 void
vchan_setmaxauto(struct snddev_info * d,int num)1011 vchan_setmaxauto(struct snddev_info *d, int num)
1012 {
1013 PCM_BUSYASSERT(d);
1014
1015 if (num < 0)
1016 return;
1017
1018 if (num >= 0 && d->pvchancount > num)
1019 (void)vchan_setnew(d, PCMDIR_PLAY, num);
1020 else if (num > 0 && d->pvchancount == 0)
1021 (void)vchan_setnew(d, PCMDIR_PLAY, 1);
1022
1023 if (num >= 0 && d->rvchancount > num)
1024 (void)vchan_setnew(d, PCMDIR_REC, num);
1025 else if (num > 0 && d->rvchancount == 0)
1026 (void)vchan_setnew(d, PCMDIR_REC, 1);
1027 }
1028
1029 static int
sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)1030 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
1031 {
1032 struct snddev_info *d;
1033 int i, v, error;
1034
1035 v = snd_maxautovchans;
1036 error = sysctl_handle_int(oidp, &v, 0, req);
1037 if (error == 0 && req->newptr != NULL) {
1038 if (v < 0)
1039 v = 0;
1040 if (v > SND_MAXVCHANS)
1041 v = SND_MAXVCHANS;
1042 snd_maxautovchans = v;
1043 for (i = 0; pcm_devclass != NULL &&
1044 i < devclass_get_maxunit(pcm_devclass); i++) {
1045 d = devclass_get_softc(pcm_devclass, i);
1046 if (!PCM_REGISTERED(d))
1047 continue;
1048 PCM_ACQUIRE_QUICK(d);
1049 vchan_setmaxauto(d, v);
1050 PCM_RELEASE_QUICK(d);
1051 }
1052 }
1053 return (error);
1054 }
1055 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans,
1056 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
1057 sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
1058
1059 void
vchan_initsys(device_t dev)1060 vchan_initsys(device_t dev)
1061 {
1062 struct snddev_info *d;
1063 int unit;
1064
1065 unit = device_get_unit(dev);
1066 d = device_get_softc(dev);
1067
1068 /* Play */
1069 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1070 SYSCTL_CHILDREN(d->play_sysctl_tree),
1071 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1072 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1073 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
1074 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1075 SYSCTL_CHILDREN(d->play_sysctl_tree),
1076 OID_AUTO, "vchanmode",
1077 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1078 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1079 sysctl_dev_pcm_vchanmode, "A",
1080 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1081 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1082 SYSCTL_CHILDREN(d->play_sysctl_tree),
1083 OID_AUTO, "vchanrate",
1084 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1085 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1086 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1087 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1088 SYSCTL_CHILDREN(d->play_sysctl_tree),
1089 OID_AUTO, "vchanformat",
1090 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1091 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1092 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1093 /* Rec */
1094 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1095 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1096 OID_AUTO, "vchans",
1097 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1098 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1099 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
1100 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1101 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1102 OID_AUTO, "vchanmode",
1103 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1104 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1105 sysctl_dev_pcm_vchanmode, "A",
1106 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1107 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1108 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1109 OID_AUTO, "vchanrate",
1110 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1111 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1112 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1113 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1114 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1115 OID_AUTO, "vchanformat",
1116 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1117 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1118 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1119 }
1120