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