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 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 void
vchan_getparentchannel(struct snddev_info * d,struct pcm_channel ** wrch,struct pcm_channel ** rdch)257 vchan_getparentchannel(struct snddev_info *d,
258 struct pcm_channel **wrch, struct pcm_channel **rdch)
259 {
260 struct pcm_channel **ch, *wch, *rch, *c;
261
262 KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__));
263
264 PCM_BUSYASSERT(d);
265 PCM_UNLOCKASSERT(d);
266
267 wch = NULL;
268 rch = NULL;
269
270 CHN_FOREACH(c, d, channels.pcm) {
271 CHN_LOCK(c);
272 ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch;
273 if (c->flags & CHN_F_VIRTUAL) {
274 /* Sanity check */
275 if (*ch != NULL && *ch != c->parentchannel) {
276 CHN_UNLOCK(c);
277 *ch = NULL;
278 break;
279 }
280 } else if (c->flags & CHN_F_HAS_VCHAN) {
281 /* No way!! */
282 if (*ch != NULL) {
283 CHN_UNLOCK(c);
284 *ch = NULL;
285 break;
286 }
287 *ch = c;
288 }
289 CHN_UNLOCK(c);
290 }
291
292 if (wrch != NULL)
293 *wrch = wch;
294 if (rdch != NULL)
295 *rdch = rch;
296 }
297
298 static int
sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)299 sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
300 {
301 struct snddev_info *d;
302 int direction, vchancount;
303 int err, cnt;
304
305 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
306 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
307 return (EINVAL);
308
309 PCM_LOCK(d);
310 PCM_WAIT(d);
311
312 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
313 case VCHAN_PLAY:
314 direction = PCMDIR_PLAY;
315 vchancount = d->pvchancount;
316 cnt = d->playcount;
317 break;
318 case VCHAN_REC:
319 direction = PCMDIR_REC;
320 vchancount = d->rvchancount;
321 cnt = d->reccount;
322 break;
323 default:
324 PCM_UNLOCK(d);
325 return (EINVAL);
326 break;
327 }
328
329 if (cnt < 1) {
330 PCM_UNLOCK(d);
331 return (ENODEV);
332 }
333
334 PCM_ACQUIRE(d);
335 PCM_UNLOCK(d);
336
337 cnt = vchancount;
338 err = sysctl_handle_int(oidp, &cnt, 0, req);
339
340 if (err == 0 && req->newptr != NULL && vchancount != cnt) {
341 if (cnt < 0)
342 cnt = 0;
343 if (cnt > SND_MAXVCHANS)
344 cnt = SND_MAXVCHANS;
345 err = vchan_setnew(d, direction, cnt);
346 }
347
348 PCM_RELEASE_QUICK(d);
349
350 return err;
351 }
352
353 static int
sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)354 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
355 {
356 struct snddev_info *d;
357 struct pcm_channel *c;
358 uint32_t dflags;
359 int direction, ret;
360 char dtype[16];
361
362 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
363 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
364 return (EINVAL);
365
366 PCM_LOCK(d);
367 PCM_WAIT(d);
368
369 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
370 case VCHAN_PLAY:
371 direction = PCMDIR_PLAY;
372 break;
373 case VCHAN_REC:
374 direction = PCMDIR_REC;
375 break;
376 default:
377 PCM_UNLOCK(d);
378 return (EINVAL);
379 break;
380 }
381
382 PCM_ACQUIRE(d);
383 PCM_UNLOCK(d);
384
385 if (direction == PCMDIR_PLAY)
386 vchan_getparentchannel(d, &c, NULL);
387 else
388 vchan_getparentchannel(d, NULL, &c);
389
390 if (c == NULL) {
391 PCM_RELEASE_QUICK(d);
392 return (EINVAL);
393 }
394
395 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
396 __func__, direction, c->direction));
397
398 CHN_LOCK(c);
399 if (c->flags & CHN_F_VCHAN_PASSTHROUGH)
400 strlcpy(dtype, "passthrough", sizeof(dtype));
401 else if (c->flags & CHN_F_VCHAN_ADAPTIVE)
402 strlcpy(dtype, "adaptive", sizeof(dtype));
403 else
404 strlcpy(dtype, "fixed", sizeof(dtype));
405 CHN_UNLOCK(c);
406
407 ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req);
408 if (ret == 0 && req->newptr != NULL) {
409 if (strcasecmp(dtype, "passthrough") == 0 ||
410 strcmp(dtype, "1") == 0)
411 dflags = CHN_F_VCHAN_PASSTHROUGH;
412 else if (strcasecmp(dtype, "adaptive") == 0 ||
413 strcmp(dtype, "2") == 0)
414 dflags = CHN_F_VCHAN_ADAPTIVE;
415 else if (strcasecmp(dtype, "fixed") == 0 ||
416 strcmp(dtype, "0") == 0)
417 dflags = 0;
418 else {
419 PCM_RELEASE_QUICK(d);
420 return (EINVAL);
421 }
422 CHN_LOCK(c);
423 if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) ||
424 (c->flags & CHN_F_PASSTHROUGH)) {
425 CHN_UNLOCK(c);
426 PCM_RELEASE_QUICK(d);
427 return (0);
428 }
429 c->flags &= ~CHN_F_VCHAN_DYNAMIC;
430 c->flags |= dflags;
431 CHN_UNLOCK(c);
432 }
433
434 PCM_RELEASE_QUICK(d);
435
436 return (ret);
437 }
438
439 /*
440 * On the fly vchan rate/format settings
441 */
442
443 #define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \
444 CHN_F_EXCLUSIVE)) && \
445 (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \
446 CHN_STOPPED(c)))
447 static int
sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)448 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
449 {
450 struct snddev_info *d;
451 struct pcm_channel *c, *ch;
452 struct pcmchan_caps *caps;
453 int *vchanrate, vchancount, direction, ret, newspd, restart;
454
455 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
456 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
457 return (EINVAL);
458
459 PCM_LOCK(d);
460 PCM_WAIT(d);
461
462 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
463 case VCHAN_PLAY:
464 direction = PCMDIR_PLAY;
465 vchancount = d->pvchancount;
466 vchanrate = &d->pvchanrate;
467 break;
468 case VCHAN_REC:
469 direction = PCMDIR_REC;
470 vchancount = d->rvchancount;
471 vchanrate = &d->rvchanrate;
472 break;
473 default:
474 PCM_UNLOCK(d);
475 return (EINVAL);
476 break;
477 }
478
479 if (vchancount < 1) {
480 PCM_UNLOCK(d);
481 return (EINVAL);
482 }
483
484 PCM_ACQUIRE(d);
485 PCM_UNLOCK(d);
486
487 if (direction == PCMDIR_PLAY)
488 vchan_getparentchannel(d, &c, NULL);
489 else
490 vchan_getparentchannel(d, NULL, &c);
491
492 if (c == NULL) {
493 PCM_RELEASE_QUICK(d);
494 return (EINVAL);
495 }
496
497 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
498 __func__, direction, c->direction));
499
500 CHN_LOCK(c);
501 newspd = c->speed;
502 CHN_UNLOCK(c);
503
504 ret = sysctl_handle_int(oidp, &newspd, 0, req);
505 if (ret != 0 || req->newptr == NULL) {
506 PCM_RELEASE_QUICK(d);
507 return (ret);
508 }
509
510 if (newspd < feeder_rate_min || newspd > feeder_rate_max) {
511 PCM_RELEASE_QUICK(d);
512 return (EINVAL);
513 }
514
515 CHN_LOCK(c);
516
517 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) {
518 if (CHN_STARTED(c)) {
519 chn_abort(c);
520 restart = 1;
521 } else
522 restart = 0;
523
524 if (feeder_rate_round) {
525 caps = chn_getcaps(c);
526 RANGE(newspd, caps->minspeed, caps->maxspeed);
527 newspd = CHANNEL_SETSPEED(c->methods,
528 c->devinfo, newspd);
529 }
530
531 ret = chn_reset(c, c->format, newspd);
532 if (ret == 0) {
533 *vchanrate = c->speed;
534 if (restart != 0) {
535 CHN_FOREACH(ch, c, children.busy) {
536 CHN_LOCK(ch);
537 if (VCHAN_SYNC_REQUIRED(ch))
538 vchan_sync(ch);
539 CHN_UNLOCK(ch);
540 }
541 c->flags |= CHN_F_DIRTY;
542 ret = chn_start(c, 1);
543 }
544 }
545 }
546
547 CHN_UNLOCK(c);
548
549 PCM_RELEASE_QUICK(d);
550
551 return (ret);
552 }
553
554 static int
sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)555 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
556 {
557 struct snddev_info *d;
558 struct pcm_channel *c, *ch;
559 uint32_t newfmt;
560 int *vchanformat, vchancount, direction, ret, restart;
561 char fmtstr[AFMTSTR_LEN];
562
563 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
564 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
565 return (EINVAL);
566
567 PCM_LOCK(d);
568 PCM_WAIT(d);
569
570 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
571 case VCHAN_PLAY:
572 direction = PCMDIR_PLAY;
573 vchancount = d->pvchancount;
574 vchanformat = &d->pvchanformat;
575 break;
576 case VCHAN_REC:
577 direction = PCMDIR_REC;
578 vchancount = d->rvchancount;
579 vchanformat = &d->rvchanformat;
580 break;
581 default:
582 PCM_UNLOCK(d);
583 return (EINVAL);
584 break;
585 }
586
587 if (vchancount < 1) {
588 PCM_UNLOCK(d);
589 return (EINVAL);
590 }
591
592 PCM_ACQUIRE(d);
593 PCM_UNLOCK(d);
594
595 if (direction == PCMDIR_PLAY)
596 vchan_getparentchannel(d, &c, NULL);
597 else
598 vchan_getparentchannel(d, NULL, &c);
599
600 if (c == NULL) {
601 PCM_RELEASE_QUICK(d);
602 return (EINVAL);
603 }
604
605 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
606 __func__, direction, c->direction));
607
608 CHN_LOCK(c);
609
610 bzero(fmtstr, sizeof(fmtstr));
611
612 if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format)
613 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr));
614
615 CHN_UNLOCK(c);
616
617 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
618 if (ret != 0 || req->newptr == NULL) {
619 PCM_RELEASE_QUICK(d);
620 return (ret);
621 }
622
623 newfmt = snd_str2afmt(fmtstr);
624 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) {
625 PCM_RELEASE_QUICK(d);
626 return (EINVAL);
627 }
628
629 CHN_LOCK(c);
630
631 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) {
632 if (CHN_STARTED(c)) {
633 chn_abort(c);
634 restart = 1;
635 } else
636 restart = 0;
637
638 ret = chn_reset(c, newfmt, c->speed);
639 if (ret == 0) {
640 *vchanformat = c->format;
641 if (restart != 0) {
642 CHN_FOREACH(ch, c, children.busy) {
643 CHN_LOCK(ch);
644 if (VCHAN_SYNC_REQUIRED(ch))
645 vchan_sync(ch);
646 CHN_UNLOCK(ch);
647 }
648 c->flags |= CHN_F_DIRTY;
649 ret = chn_start(c, 1);
650 }
651 }
652 }
653
654 CHN_UNLOCK(c);
655
656 PCM_RELEASE_QUICK(d);
657
658 return (ret);
659 }
660
661 /* virtual channel interface */
662
663 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
664 "play.vchanformat" : "rec.vchanformat"
665 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
666 "play.vchanrate" : "rec.vchanrate"
667
668 int
vchan_create(struct pcm_channel * parent)669 vchan_create(struct pcm_channel *parent)
670 {
671 struct snddev_info *d;
672 struct pcm_channel *ch;
673 struct pcmchan_caps *parent_caps;
674 uint32_t vchanfmt, vchanspd;
675 int ret, direction, r;
676 bool save;
677
678 ret = 0;
679 save = false;
680 d = parent->parentsnddev;
681
682 PCM_BUSYASSERT(d);
683 CHN_LOCKASSERT(parent);
684
685 if (!(parent->flags & CHN_F_BUSY))
686 return (EBUSY);
687
688 if (!(parent->direction == PCMDIR_PLAY ||
689 parent->direction == PCMDIR_REC))
690 return (EINVAL);
691
692 CHN_UNLOCK(parent);
693 PCM_LOCK(d);
694
695 if (parent->direction == PCMDIR_PLAY) {
696 direction = PCMDIR_PLAY_VIRTUAL;
697 vchanfmt = d->pvchanformat;
698 vchanspd = d->pvchanrate;
699 } else {
700 direction = PCMDIR_REC_VIRTUAL;
701 vchanfmt = d->rvchanformat;
702 vchanspd = d->rvchanrate;
703 }
704
705 /* create a new playback channel */
706 ch = chn_init(d, parent, &vchan_class, direction, parent);
707 if (ch == NULL) {
708 PCM_UNLOCK(d);
709 CHN_LOCK(parent);
710 return (ENODEV);
711 }
712 PCM_UNLOCK(d);
713
714 CHN_LOCK(parent);
715 /*
716 * Add us to our parent channel's children in reverse order
717 * so future destruction will pick the last (biggest number)
718 * channel.
719 */
720 CHN_INSERT_SORT_DESCEND(parent, ch, children);
721
722 if (parent->flags & CHN_F_HAS_VCHAN)
723 return (0);
724
725 parent->flags |= CHN_F_HAS_VCHAN;
726
727 parent_caps = chn_getcaps(parent);
728 if (parent_caps == NULL) {
729 ret = EINVAL;
730 goto fail;
731 }
732
733 if (vchanfmt == 0) {
734 const char *vfmt;
735
736 CHN_UNLOCK(parent);
737 r = resource_string_value(device_get_name(parent->dev),
738 device_get_unit(parent->dev), VCHAN_FMT_HINT(direction),
739 &vfmt);
740 CHN_LOCK(parent);
741 if (r != 0)
742 vfmt = NULL;
743 if (vfmt != NULL) {
744 vchanfmt = snd_str2afmt(vfmt);
745 if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN))
746 vchanfmt = 0;
747 }
748 if (vchanfmt == 0)
749 vchanfmt = VCHAN_DEFAULT_FORMAT;
750 save = true;
751 }
752
753 if (vchanspd == 0) {
754 /*
755 * This is very sad. Few soundcards advertised as being
756 * able to do (insanely) higher/lower speed, but in
757 * reality, they simply can't. At least, we give user chance
758 * to set sane value via kernel hints or sysctl.
759 */
760 CHN_UNLOCK(parent);
761 r = resource_int_value(device_get_name(parent->dev),
762 device_get_unit(parent->dev), VCHAN_SPD_HINT(direction),
763 &vchanspd);
764 CHN_LOCK(parent);
765 if (r != 0) {
766 /* No saved value, no hint, NOTHING. */
767 vchanspd = VCHAN_DEFAULT_RATE;
768 RANGE(vchanspd, parent_caps->minspeed,
769 parent_caps->maxspeed);
770 }
771 save = true;
772 }
773
774 /*
775 * Limit the speed between feeder_rate_min <-> feeder_rate_max.
776 */
777 RANGE(vchanspd, feeder_rate_min, feeder_rate_max);
778
779 if (feeder_rate_round) {
780 RANGE(vchanspd, parent_caps->minspeed,
781 parent_caps->maxspeed);
782 vchanspd = CHANNEL_SETSPEED(parent->methods,
783 parent->devinfo, vchanspd);
784 }
785
786 if ((ret = chn_reset(parent, vchanfmt, vchanspd)) != 0)
787 goto fail;
788
789 if (save) {
790 /*
791 * Save new value.
792 */
793 if (direction == PCMDIR_PLAY_VIRTUAL) {
794 d->pvchanformat = parent->format;
795 d->pvchanrate = parent->speed;
796 } else {
797 d->rvchanformat = parent->format;
798 d->rvchanrate = parent->speed;
799 }
800 }
801
802 /*
803 * If the parent channel supports digital format,
804 * enable passthrough mode.
805 */
806 if (snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
807 parent->flags &= ~CHN_F_VCHAN_DYNAMIC;
808 parent->flags |= CHN_F_VCHAN_PASSTHROUGH;
809 }
810
811 return (ret);
812
813 fail:
814 CHN_REMOVE(parent, ch, children);
815 parent->flags &= ~CHN_F_HAS_VCHAN;
816 CHN_UNLOCK(parent);
817 chn_kill(ch);
818 CHN_LOCK(parent);
819
820 return (ret);
821 }
822
823 int
vchan_destroy(struct pcm_channel * c)824 vchan_destroy(struct pcm_channel *c)
825 {
826 struct pcm_channel *parent;
827
828 KASSERT(c != NULL && c->parentchannel != NULL &&
829 c->parentsnddev != NULL, ("%s(): invalid channel=%p",
830 __func__, c));
831
832 CHN_LOCKASSERT(c);
833
834 parent = c->parentchannel;
835
836 PCM_BUSYASSERT(c->parentsnddev);
837 CHN_LOCKASSERT(parent);
838
839 CHN_UNLOCK(c);
840
841 if (!(parent->flags & CHN_F_BUSY))
842 return (EBUSY);
843
844 if (CHN_EMPTY(parent, children))
845 return (EINVAL);
846
847 /* remove us from our parent's children list */
848 CHN_REMOVE(parent, c, children);
849
850 if (CHN_EMPTY(parent, children)) {
851 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
852 chn_reset(parent, parent->format, parent->speed);
853 }
854
855 CHN_UNLOCK(parent);
856
857 /* destroy ourselves */
858 chn_kill(c);
859
860 CHN_LOCK(parent);
861
862 return (0);
863 }
864
865 int
866 #ifdef SND_DEBUG
vchan_passthrough(struct pcm_channel * c,const char * caller)867 vchan_passthrough(struct pcm_channel *c, const char *caller)
868 #else
869 vchan_sync(struct pcm_channel *c)
870 #endif
871 {
872 int ret;
873
874 KASSERT(c != NULL && c->parentchannel != NULL &&
875 (c->flags & CHN_F_VIRTUAL),
876 ("%s(): invalid passthrough", __func__));
877 CHN_LOCKASSERT(c);
878 CHN_LOCKASSERT(c->parentchannel);
879
880 sndbuf_setspd(c->bufhard, c->parentchannel->speed);
881 c->flags |= CHN_F_PASSTHROUGH;
882 ret = feeder_chain(c);
883 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH);
884 if (ret != 0)
885 c->flags |= CHN_F_DIRTY;
886
887 #ifdef SND_DEBUG
888 if (snd_passthrough_verbose) {
889 device_printf(c->dev, "%s(%s/%s) %s() -> re-sync err=%d\n",
890 __func__, c->name, c->comm, caller, ret);
891 }
892 #endif
893
894 return (ret);
895 }
896
897 int
vchan_setnew(struct snddev_info * d,int direction,int newcnt)898 vchan_setnew(struct snddev_info *d, int direction, int newcnt)
899 {
900 struct pcm_channel *c, *ch, *nch;
901 struct pcmchan_caps *caps;
902 int i, err, vcnt;
903
904 PCM_BUSYASSERT(d);
905
906 if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
907 (direction == PCMDIR_REC && d->reccount < 1))
908 return (ENODEV);
909
910 if (!(d->flags & SD_F_AUTOVCHAN))
911 return (EINVAL);
912
913 if (newcnt < 0 || newcnt > SND_MAXVCHANS)
914 return (E2BIG);
915
916 if (direction == PCMDIR_PLAY)
917 vcnt = d->pvchancount;
918 else if (direction == PCMDIR_REC)
919 vcnt = d->rvchancount;
920 else
921 return (EINVAL);
922
923 if (newcnt > vcnt) {
924 /* add new vchans - find a parent channel first */
925 ch = NULL;
926 CHN_FOREACH(c, d, channels.pcm) {
927 CHN_LOCK(c);
928 if (c->direction == direction &&
929 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
930 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
931 /*
932 * Reuse hw channel with vchans already
933 * created.
934 */
935 if (c->flags & CHN_F_HAS_VCHAN) {
936 ch = c;
937 break;
938 }
939 /*
940 * No vchans ever created, look for
941 * channels with supported formats.
942 */
943 caps = chn_getcaps(c);
944 if (caps == NULL) {
945 CHN_UNLOCK(c);
946 continue;
947 }
948 for (i = 0; caps->fmtlist[i] != 0; i++) {
949 if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
950 break;
951 }
952 if (caps->fmtlist[i] != 0) {
953 ch = c;
954 break;
955 }
956 }
957 CHN_UNLOCK(c);
958 }
959 if (ch == NULL)
960 return (EBUSY);
961 ch->flags |= CHN_F_BUSY;
962 err = 0;
963 while (err == 0 && newcnt > vcnt) {
964 err = vchan_create(ch);
965 if (err == 0)
966 vcnt++;
967 else if (err == E2BIG && newcnt > vcnt)
968 device_printf(d->dev,
969 "%s: err=%d Maximum channel reached.\n",
970 __func__, err);
971 }
972 if (vcnt == 0)
973 ch->flags &= ~CHN_F_BUSY;
974 CHN_UNLOCK(ch);
975 if (err != 0)
976 return (err);
977 } else if (newcnt < vcnt) {
978 CHN_FOREACH(c, d, channels.pcm) {
979 CHN_LOCK(c);
980 if (c->direction != direction ||
981 CHN_EMPTY(c, children) ||
982 !(c->flags & CHN_F_HAS_VCHAN)) {
983 CHN_UNLOCK(c);
984 continue;
985 }
986 CHN_FOREACH_SAFE(ch, c, nch, children) {
987 CHN_LOCK(ch);
988 if (vcnt == 1 && ch->flags & CHN_F_BUSY) {
989 CHN_UNLOCK(ch);
990 break;
991 }
992 if (!(ch->flags & CHN_F_BUSY)) {
993 err = vchan_destroy(ch);
994 if (err == 0)
995 vcnt--;
996 } else
997 CHN_UNLOCK(ch);
998 if (vcnt == newcnt)
999 break;
1000 }
1001 CHN_UNLOCK(c);
1002 break;
1003 }
1004 }
1005
1006 return (0);
1007 }
1008
1009 void
vchan_setmaxauto(struct snddev_info * d,int num)1010 vchan_setmaxauto(struct snddev_info *d, int num)
1011 {
1012 PCM_BUSYASSERT(d);
1013
1014 if (num < 0)
1015 return;
1016
1017 if (num >= 0 && d->pvchancount > num)
1018 (void)vchan_setnew(d, PCMDIR_PLAY, num);
1019 else if (num > 0 && d->pvchancount == 0)
1020 (void)vchan_setnew(d, PCMDIR_PLAY, 1);
1021
1022 if (num >= 0 && d->rvchancount > num)
1023 (void)vchan_setnew(d, PCMDIR_REC, num);
1024 else if (num > 0 && d->rvchancount == 0)
1025 (void)vchan_setnew(d, PCMDIR_REC, 1);
1026 }
1027
1028 static int
sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)1029 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
1030 {
1031 struct snddev_info *d;
1032 int i, v, error;
1033
1034 v = snd_maxautovchans;
1035 error = sysctl_handle_int(oidp, &v, 0, req);
1036 if (error == 0 && req->newptr != NULL) {
1037 if (v < 0)
1038 v = 0;
1039 if (v > SND_MAXVCHANS)
1040 v = SND_MAXVCHANS;
1041 snd_maxautovchans = v;
1042 for (i = 0; pcm_devclass != NULL &&
1043 i < devclass_get_maxunit(pcm_devclass); i++) {
1044 d = devclass_get_softc(pcm_devclass, i);
1045 if (!PCM_REGISTERED(d))
1046 continue;
1047 PCM_ACQUIRE_QUICK(d);
1048 vchan_setmaxauto(d, v);
1049 PCM_RELEASE_QUICK(d);
1050 }
1051 }
1052 return (error);
1053 }
1054 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans,
1055 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
1056 sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
1057
1058 void
vchan_initsys(device_t dev)1059 vchan_initsys(device_t dev)
1060 {
1061 struct snddev_info *d;
1062 int unit;
1063
1064 unit = device_get_unit(dev);
1065 d = device_get_softc(dev);
1066
1067 /* Play */
1068 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1069 SYSCTL_CHILDREN(d->play_sysctl_tree),
1070 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1071 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1072 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
1073 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1074 SYSCTL_CHILDREN(d->play_sysctl_tree),
1075 OID_AUTO, "vchanmode",
1076 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1077 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1078 sysctl_dev_pcm_vchanmode, "A",
1079 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1080 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1081 SYSCTL_CHILDREN(d->play_sysctl_tree),
1082 OID_AUTO, "vchanrate",
1083 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1084 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1085 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1086 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1087 SYSCTL_CHILDREN(d->play_sysctl_tree),
1088 OID_AUTO, "vchanformat",
1089 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1090 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1091 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1092 /* Rec */
1093 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1094 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1095 OID_AUTO, "vchans",
1096 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1097 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1098 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
1099 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1100 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1101 OID_AUTO, "vchanmode",
1102 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1103 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1104 sysctl_dev_pcm_vchanmode, "A",
1105 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1106 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1107 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1108 OID_AUTO, "vchanrate",
1109 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1110 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1111 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1112 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1113 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1114 OID_AUTO, "vchanformat",
1115 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1116 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1117 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1118 }
1119