FreeBSD kernel sound device code
vchan.c
Go to the documentation of this file.
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/* Almost entirely rewritten to add multi-format/channels mixing support. */
31
32#ifdef HAVE_KERNEL_OPTION_HEADERS
33#include "opt_snd.h"
34#endif
35
36#include <dev/sound/pcm/sound.h>
37#include <dev/sound/pcm/vchan.h>
38
39SND_DECLARE_FILE("$FreeBSD$");
40
41/*
42 * [ac3 , dts , linear , 0, linear, 0]
43 */
44#define FMTLIST_MAX 6
45#define FMTLIST_OFFSET 4
46#define DIGFMTS_MAX 2
47
48#ifdef SND_DEBUG
49static int snd_passthrough_verbose = 0;
50SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN,
51 &snd_passthrough_verbose, 0, "passthrough verbosity");
52
53#endif
54
55struct vchan_info {
60};
61
62static void *
63vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
64 struct pcm_channel *c, int dir)
65{
66 struct vchan_info *info;
67 struct pcm_channel *p;
68 uint32_t i, j, *fmtlist;
69
70 KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC,
71 ("vchan_init: bad direction"));
72 KASSERT(c != NULL && c->parentchannel != NULL,
73 ("vchan_init: bad channels"));
74
75 info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO);
76 info->channel = c;
77 info->trigger = PCMTRIG_STOP;
78 p = c->parentchannel;
79
80 CHN_LOCK(p);
81
82 fmtlist = chn_getcaps(p)->fmtlist;
83 for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) {
84 if (fmtlist[i] & AFMT_PASSTHROUGH)
85 info->fmtlist[j++] = fmtlist[i];
86 }
87 if (p->format & AFMT_VCHAN)
88 info->fmtlist[j] = p->format;
89 else
91 info->caps.fmtlist = info->fmtlist +
93
94 CHN_UNLOCK(p);
95
97
98 return (info);
99}
100
101static int
102vchan_free(kobj_t obj, void *data)
103{
104
105 free(data, M_DEVBUF);
106
107 return (0);
108}
109
110static int
111vchan_setformat(kobj_t obj, void *data, uint32_t format)
112{
113 struct vchan_info *info;
114
115 info = data;
116
117 CHN_LOCKASSERT(info->channel);
118
119 if (!snd_fmtvalid(format, info->caps.fmtlist))
120 return (-1);
121
122 return (0);
123}
124
125static uint32_t
126vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
127{
128 struct vchan_info *info;
129
130 info = data;
131
132 CHN_LOCKASSERT(info->channel);
133
134 return (info->caps.maxspeed);
135}
136
137static int
138vchan_trigger(kobj_t obj, void *data, int go)
139{
140 struct vchan_info *info;
141 struct pcm_channel *c, *p;
142 int ret, otrigger;
143
144 info = data;
145
146 if (!PCMTRIG_COMMON(go) || go == info->trigger)
147 return (0);
148
149 c = info->channel;
150 p = c->parentchannel;
151 otrigger = info->trigger;
152 info->trigger = go;
153
155
156 CHN_UNLOCK(c);
157 CHN_LOCK(p);
158
159 switch (go) {
160 case PCMTRIG_START:
161 if (otrigger != PCMTRIG_START)
162 CHN_INSERT_HEAD(p, c, children.busy);
163 break;
164 case PCMTRIG_STOP:
165 case PCMTRIG_ABORT:
166 if (otrigger == PCMTRIG_START)
167 CHN_REMOVE(p, c, children.busy);
168 break;
169 default:
170 break;
171 }
172
173 ret = chn_notify(p, CHN_N_TRIGGER);
174
175 CHN_LOCK(c);
176
177 if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c))
178 ret = vchan_sync(c);
179
180 CHN_UNLOCK(c);
181 CHN_UNLOCK(p);
182 CHN_LOCK(c);
183
184 return (ret);
185}
186
187static struct pcmchan_caps *
188vchan_getcaps(kobj_t obj, void *data)
189{
190 struct vchan_info *info;
191 struct pcm_channel *c;
192 uint32_t pformat, pspeed, pflags, i;
193
194 info = data;
195 c = info->channel;
196 pformat = c->parentchannel->format;
197 pspeed = c->parentchannel->speed;
198 pflags = c->parentchannel->flags;
199
201
202 if (pflags & CHN_F_VCHAN_DYNAMIC) {
203 info->caps.fmtlist = info->fmtlist;
204 if (pformat & AFMT_VCHAN) {
205 for (i = 0; info->caps.fmtlist[i] != 0; i++) {
206 if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH)
207 continue;
208 break;
209 }
210 info->caps.fmtlist[i] = pformat;
211 }
213 info->caps.minspeed = c->speed;
214 else
215 info->caps.minspeed = pspeed;
216 info->caps.maxspeed = info->caps.minspeed;
217 } else {
218 info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET;
219 if (pformat & AFMT_VCHAN)
220 info->caps.fmtlist[0] = pformat;
221 else {
222 device_printf(c->dev,
223 "%s(): invalid vchan format 0x%08x",
224 __func__, pformat);
226 }
227 info->caps.minspeed = pspeed;
228 info->caps.maxspeed = info->caps.minspeed;
229 }
230
231 return (&info->caps);
232}
233
234static struct pcmchan_matrix *
235vchan_getmatrix(kobj_t obj, void *data, uint32_t format)
236{
237
239}
240
241static kobj_method_t vchan_methods[] = {
242 KOBJMETHOD(channel_init, vchan_init),
243 KOBJMETHOD(channel_free, vchan_free),
244 KOBJMETHOD(channel_setformat, vchan_setformat),
245 KOBJMETHOD(channel_setspeed, vchan_setspeed),
246 KOBJMETHOD(channel_trigger, vchan_trigger),
247 KOBJMETHOD(channel_getcaps, vchan_getcaps),
248 KOBJMETHOD(channel_getmatrix, vchan_getmatrix),
250};
252
253static void
255 struct pcm_channel **wrch, struct pcm_channel **rdch)
256{
257 struct pcm_channel **ch, *wch, *rch, *c;
258
259 KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__));
260
263
264 wch = NULL;
265 rch = NULL;
266
267 CHN_FOREACH(c, d, channels.pcm) {
268 CHN_LOCK(c);
269 ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch;
270 if (c->flags & CHN_F_VIRTUAL) {
271 /* Sanity check */
272 if (*ch != NULL && *ch != c->parentchannel) {
273 CHN_UNLOCK(c);
274 *ch = NULL;
275 break;
276 }
277 } else if (c->flags & CHN_F_HAS_VCHAN) {
278 /* No way!! */
279 if (*ch != NULL) {
280 CHN_UNLOCK(c);
281 *ch = NULL;
282 break;
283 }
284 *ch = c;
285 }
286 CHN_UNLOCK(c);
287 }
288
289 if (wrch != NULL)
290 *wrch = wch;
291 if (rdch != NULL)
292 *rdch = rch;
293}
294
295static int
296sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
297{
298 struct snddev_info *d;
299 int direction, vchancount;
300 int err, cnt;
301
302 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
303 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
304 return (EINVAL);
305
306 PCM_LOCK(d);
307 PCM_WAIT(d);
308
309 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
310 case VCHAN_PLAY:
311 direction = PCMDIR_PLAY;
312 vchancount = d->pvchancount;
313 cnt = d->playcount;
314 break;
315 case VCHAN_REC:
316 direction = PCMDIR_REC;
317 vchancount = d->rvchancount;
318 cnt = d->reccount;
319 break;
320 default:
321 PCM_UNLOCK(d);
322 return (EINVAL);
323 break;
324 }
325
326 if (cnt < 1) {
327 PCM_UNLOCK(d);
328 return (ENODEV);
329 }
330
331 PCM_ACQUIRE(d);
332 PCM_UNLOCK(d);
333
334 cnt = vchancount;
335 err = sysctl_handle_int(oidp, &cnt, 0, req);
336
337 if (err == 0 && req->newptr != NULL && vchancount != cnt) {
338 if (cnt < 0)
339 cnt = 0;
340 if (cnt > SND_MAXVCHANS)
341 cnt = SND_MAXVCHANS;
342 err = pcm_setvchans(d, direction, cnt, -1);
343 }
344
346
347 return err;
348}
349
350static int
351sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
352{
353 struct snddev_info *d;
354 struct pcm_channel *c;
355 uint32_t dflags;
356 int direction, ret;
357 char dtype[16];
358
359 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
360 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
361 return (EINVAL);
362
363 PCM_LOCK(d);
364 PCM_WAIT(d);
365
366 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
367 case VCHAN_PLAY:
369 break;
370 case VCHAN_REC:
372 break;
373 default:
374 PCM_UNLOCK(d);
375 return (EINVAL);
376 break;
377 }
378
379 PCM_ACQUIRE(d);
380 PCM_UNLOCK(d);
381
382 if (direction == PCMDIR_PLAY)
383 pcm_getparentchannel(d, &c, NULL);
384 else
385 pcm_getparentchannel(d, NULL, &c);
386
387 if (c == NULL) {
389 return (EINVAL);
390 }
391
392 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
393 __func__, direction, c->direction));
394
395 CHN_LOCK(c);
397 strlcpy(dtype, "passthrough", sizeof(dtype));
398 else if (c->flags & CHN_F_VCHAN_ADAPTIVE)
399 strlcpy(dtype, "adaptive", sizeof(dtype));
400 else
401 strlcpy(dtype, "fixed", sizeof(dtype));
402 CHN_UNLOCK(c);
403
404 ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req);
405 if (ret == 0 && req->newptr != NULL) {
406 if (strcasecmp(dtype, "passthrough") == 0 ||
407 strcmp(dtype, "1") == 0)
409 else if (strcasecmp(dtype, "adaptive") == 0 ||
410 strcmp(dtype, "2") == 0)
411 dflags = CHN_F_VCHAN_ADAPTIVE;
412 else if (strcasecmp(dtype, "fixed") == 0 ||
413 strcmp(dtype, "0") == 0)
414 dflags = 0;
415 else {
417 return (EINVAL);
418 }
419 CHN_LOCK(c);
420 if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) ||
422 CHN_UNLOCK(c);
424 return (0);
425 }
426 c->flags &= ~CHN_F_VCHAN_DYNAMIC;
427 c->flags |= dflags;
428 CHN_UNLOCK(c);
429 }
430
432
433 return (ret);
434}
435
436/*
437 * On the fly vchan rate/format settings
438 */
439
440#define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \
441 CHN_F_EXCLUSIVE)) && \
442 (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \
443 CHN_STOPPED(c)))
444static int
445sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
446{
447 struct snddev_info *d;
448 struct pcm_channel *c, *ch;
449 struct pcmchan_caps *caps;
450 int *vchanrate, vchancount, direction, ret, newspd, restart;
451
452 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
453 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
454 return (EINVAL);
455
456 PCM_LOCK(d);
457 PCM_WAIT(d);
458
459 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
460 case VCHAN_PLAY:
461 direction = PCMDIR_PLAY;
462 vchancount = d->pvchancount;
463 vchanrate = &d->pvchanrate;
464 break;
465 case VCHAN_REC:
466 direction = PCMDIR_REC;
467 vchancount = d->rvchancount;
468 vchanrate = &d->rvchanrate;
469 break;
470 default:
471 PCM_UNLOCK(d);
472 return (EINVAL);
473 break;
474 }
475
476 if (vchancount < 1) {
477 PCM_UNLOCK(d);
478 return (EINVAL);
479 }
480
481 PCM_ACQUIRE(d);
482 PCM_UNLOCK(d);
483
484 if (direction == PCMDIR_PLAY)
485 pcm_getparentchannel(d, &c, NULL);
486 else
487 pcm_getparentchannel(d, NULL, &c);
488
489 if (c == NULL) {
491 return (EINVAL);
492 }
493
494 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
495 __func__, direction, c->direction));
496
497 CHN_LOCK(c);
498 newspd = c->speed;
499 CHN_UNLOCK(c);
500
501 ret = sysctl_handle_int(oidp, &newspd, 0, req);
502 if (ret != 0 || req->newptr == NULL) {
504 return (ret);
505 }
506
507 if (newspd < 1 || newspd < feeder_rate_min ||
508 newspd > feeder_rate_max) {
510 return (EINVAL);
511 }
512
513 CHN_LOCK(c);
514
515 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) {
516 if (CHN_STARTED(c)) {
517 chn_abort(c);
518 restart = 1;
519 } else
520 restart = 0;
521
522 if (feeder_rate_round) {
523 caps = chn_getcaps(c);
524 RANGE(newspd, caps->minspeed, caps->maxspeed);
525 newspd = CHANNEL_SETSPEED(c->methods,
526 c->devinfo, newspd);
527 }
528
529 ret = chn_reset(c, c->format, newspd);
530 if (ret == 0) {
531 *vchanrate = c->speed;
532 if (restart != 0) {
533 CHN_FOREACH(ch, c, children.busy) {
534 CHN_LOCK(ch);
535 if (VCHAN_SYNC_REQUIRED(ch))
536 vchan_sync(ch);
537 CHN_UNLOCK(ch);
538 }
539 c->flags |= CHN_F_DIRTY;
540 ret = chn_start(c, 1);
541 }
542 }
543 }
544
545 CHN_UNLOCK(c);
546
548
549 return (ret);
550}
551
552static int
553sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
554{
555 struct snddev_info *d;
556 struct pcm_channel *c, *ch;
557 uint32_t newfmt;
558 int *vchanformat, vchancount, direction, ret, restart;
559 char fmtstr[AFMTSTR_LEN];
560
561 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
562 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
563 return (EINVAL);
564
565 PCM_LOCK(d);
566 PCM_WAIT(d);
567
568 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
569 case VCHAN_PLAY:
571 vchancount = d->pvchancount;
572 vchanformat = &d->pvchanformat;
573 break;
574 case VCHAN_REC:
576 vchancount = d->rvchancount;
577 vchanformat = &d->rvchanformat;
578 break;
579 default:
580 PCM_UNLOCK(d);
581 return (EINVAL);
582 break;
583 }
584
585 if (vchancount < 1) {
586 PCM_UNLOCK(d);
587 return (EINVAL);
588 }
589
590 PCM_ACQUIRE(d);
591 PCM_UNLOCK(d);
592
593 if (direction == PCMDIR_PLAY)
594 pcm_getparentchannel(d, &c, NULL);
595 else
596 pcm_getparentchannel(d, NULL, &c);
597
598 if (c == NULL) {
600 return (EINVAL);
601 }
602
603 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
604 __func__, direction, c->direction));
605
606 CHN_LOCK(c);
607
608 bzero(fmtstr, sizeof(fmtstr));
609
610 if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format)
611 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr));
612
613 CHN_UNLOCK(c);
614
615 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
616 if (ret != 0 || req->newptr == NULL) {
618 return (ret);
619 }
620
621 newfmt = snd_str2afmt(fmtstr);
622 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) {
624 return (EINVAL);
625 }
626
627 CHN_LOCK(c);
628
629 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) {
630 if (CHN_STARTED(c)) {
631 chn_abort(c);
632 restart = 1;
633 } else
634 restart = 0;
635
636 ret = chn_reset(c, newfmt, c->speed);
637 if (ret == 0) {
638 *vchanformat = c->format;
639 if (restart != 0) {
640 CHN_FOREACH(ch, c, children.busy) {
641 CHN_LOCK(ch);
642 if (VCHAN_SYNC_REQUIRED(ch))
643 vchan_sync(ch);
644 CHN_UNLOCK(ch);
645 }
646 c->flags |= CHN_F_DIRTY;
647 ret = chn_start(c, 1);
648 }
649 }
650 }
651
652 CHN_UNLOCK(c);
653
655
656 return (ret);
657}
658
659/* virtual channel interface */
660
661#define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
662 "play.vchanformat" : "rec.vchanformat"
663#define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
664 "play.vchanrate" : "rec.vchanrate"
665
666int
667vchan_create(struct pcm_channel *parent, int num)
668{
669 struct snddev_info *d;
670 struct pcm_channel *ch;
671 struct pcmchan_caps *parent_caps;
672 uint32_t vchanfmt, vchanspd;
673 int ret, direction, r, save;
674
675 d = parent->parentsnddev;
676
678 CHN_LOCKASSERT(parent);
679
680 if (!(parent->flags & CHN_F_BUSY))
681 return (EBUSY);
682
683 if (!(parent->direction == PCMDIR_PLAY ||
684 parent->direction == PCMDIR_REC))
685 return (EINVAL);
686
687 d = parent->parentsnddev;
688
689 CHN_UNLOCK(parent);
690 PCM_LOCK(d);
691
692 if (parent->direction == PCMDIR_PLAY) {
693 direction = PCMDIR_PLAY_VIRTUAL;
694 vchanfmt = d->pvchanformat;
695 vchanspd = d->pvchanrate;
696 } else {
697 direction = PCMDIR_REC_VIRTUAL;
698 vchanfmt = d->rvchanformat;
699 vchanspd = d->rvchanrate;
700 }
701
702 /* create a new playback channel */
703 ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent);
704 if (ch == NULL) {
705 PCM_UNLOCK(d);
706 CHN_LOCK(parent);
707 return (ENODEV);
708 }
709
710 /* add us to our grandparent's channel list */
711 ret = pcm_chn_add(d, ch);
712 PCM_UNLOCK(d);
713 if (ret != 0) {
714 pcm_chn_destroy(ch);
715 CHN_LOCK(parent);
716 return (ret);
717 }
718
719 CHN_LOCK(parent);
720 /*
721 * Add us to our parent channel's children in reverse order
722 * so future destruction will pick the last (biggest number)
723 * channel.
724 */
725 CHN_INSERT_SORT_DESCEND(parent, ch, children);
726
727 if (parent->flags & CHN_F_HAS_VCHAN)
728 return (0);
729
730 parent->flags |= CHN_F_HAS_VCHAN;
731
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 /*
772 * No saved value, no hint, NOTHING.
773 *
774 * Workaround for sb16 running
775 * poorly at 45k / 49k.
776 */
777 switch (parent_caps->maxspeed) {
778 case 45000:
779 case 49000:
780 vchanspd = 44100;
781 break;
782 default:
783 vchanspd = VCHAN_DEFAULT_RATE;
784 if (vchanspd > parent_caps->maxspeed)
785 vchanspd = parent_caps->maxspeed;
786 break;
787 }
788 if (vchanspd < parent_caps->minspeed)
789 vchanspd = parent_caps->minspeed;
790 }
791 save = 1;
792 }
793
794 if (ret == 0) {
795 /*
796 * Limit the speed between feeder_rate_min <-> feeder_rate_max.
797 */
798 if (vchanspd < feeder_rate_min)
799 vchanspd = feeder_rate_min;
800 if (vchanspd > feeder_rate_max)
801 vchanspd = feeder_rate_max;
802
803 if (feeder_rate_round) {
804 RANGE(vchanspd, parent_caps->minspeed,
805 parent_caps->maxspeed);
806 vchanspd = CHANNEL_SETSPEED(parent->methods,
807 parent->devinfo, vchanspd);
808 }
809
810 ret = chn_reset(parent, vchanfmt, vchanspd);
811 }
812
813 if (ret == 0 && save) {
814 /*
815 * Save new value.
816 */
817 if (direction == PCMDIR_PLAY_VIRTUAL) {
818 d->pvchanformat = parent->format;
819 d->pvchanrate = parent->speed;
820 } else {
821 d->rvchanformat = parent->format;
822 d->rvchanrate = parent->speed;
823 }
824 }
825
826 /*
827 * If the parent channel supports digital format,
828 * enable passthrough mode.
829 */
830 if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
831 parent->flags &= ~CHN_F_VCHAN_DYNAMIC;
833 }
834
835 if (ret != 0) {
836 CHN_REMOVE(parent, ch, children);
837 parent->flags &= ~CHN_F_HAS_VCHAN;
838 CHN_UNLOCK(parent);
839 PCM_LOCK(d);
840 if (pcm_chn_remove(d, ch) == 0) {
841 PCM_UNLOCK(d);
842 pcm_chn_destroy(ch);
843 } else
844 PCM_UNLOCK(d);
845 CHN_LOCK(parent);
846 }
847
848 return (ret);
849}
850
851int
853{
854 struct pcm_channel *parent;
855 struct snddev_info *d;
856 int ret;
857
858 KASSERT(c != NULL && c->parentchannel != NULL &&
859 c->parentsnddev != NULL, ("%s(): invalid channel=%p",
860 __func__, c));
861
863
864 d = c->parentsnddev;
865 parent = c->parentchannel;
866
868 CHN_LOCKASSERT(parent);
869
870 CHN_UNLOCK(c);
871
872 if (!(parent->flags & CHN_F_BUSY))
873 return (EBUSY);
874
875 if (CHN_EMPTY(parent, children))
876 return (EINVAL);
877
878 /* remove us from our parent's children list */
879 CHN_REMOVE(parent, c, children);
880
881 if (CHN_EMPTY(parent, children)) {
882 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
883 chn_reset(parent, parent->format, parent->speed);
884 }
885
886 CHN_UNLOCK(parent);
887
888 /* remove us from our grandparent's channel list */
889 PCM_LOCK(d);
890 ret = pcm_chn_remove(d, c);
891 PCM_UNLOCK(d);
892
893 /* destroy ourselves */
894 if (ret == 0)
895 ret = pcm_chn_destroy(c);
896
897 CHN_LOCK(parent);
898
899 return (ret);
900}
901
902int
903#ifdef SND_DEBUG
904vchan_passthrough(struct pcm_channel *c, const char *caller)
905#else
907#endif
908{
909 int ret;
910
911 KASSERT(c != NULL && c->parentchannel != NULL &&
912 (c->flags & CHN_F_VIRTUAL),
913 ("%s(): invalid passthrough", __func__));
916
919 ret = feeder_chain(c);
921 if (ret != 0)
922 c->flags |= CHN_F_DIRTY;
923
924#ifdef SND_DEBUG
925 if (snd_passthrough_verbose != 0) {
926 char *devname, buf[CHN_NAMELEN];
927
928 devname = dsp_unit2name(buf, sizeof(buf), c->unit);
929 device_printf(c->dev,
930 "%s(%s/%s) %s() -> re-sync err=%d\n",
931 __func__, (devname != NULL) ? devname : "dspX", c->comm,
932 caller, ret);
933 }
934#endif
935
936 return (ret);
937}
938
939void
941{
942 struct snddev_info *d;
943 int unit;
944
945 unit = device_get_unit(dev);
946 d = device_get_softc(dev);
947
948 /* Play */
949 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
950 SYSCTL_CHILDREN(d->play_sysctl_tree),
951 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
953 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
954 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
955 SYSCTL_CHILDREN(d->play_sysctl_tree),
956 OID_AUTO, "vchanmode",
957 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
960 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
961 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
962 SYSCTL_CHILDREN(d->play_sysctl_tree),
963 OID_AUTO, "vchanrate",
964 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
966 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
967 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
968 SYSCTL_CHILDREN(d->play_sysctl_tree),
969 OID_AUTO, "vchanformat",
970 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
972 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
973 /* Rec */
974 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
975 SYSCTL_CHILDREN(d->rec_sysctl_tree),
976 OID_AUTO, "vchans",
977 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
979 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
980 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
981 SYSCTL_CHILDREN(d->rec_sysctl_tree),
982 OID_AUTO, "vchanmode",
983 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
986 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
987 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
988 SYSCTL_CHILDREN(d->rec_sysctl_tree),
989 OID_AUTO, "vchanrate",
990 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
992 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
993 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
994 SYSCTL_CHILDREN(d->rec_sysctl_tree),
995 OID_AUTO, "vchanformat",
996 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
998 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
999}
u_int32_t data
Definition: ac97_if.m:60
void * devinfo
Definition: ac97_if.m:47
uint32_t format
Definition: audio_dai_if.m:39
uint32_t speed
Definition: audio_dai_if.m:86
int go
Definition: audio_dai_if.m:64
void sndbuf_setspd(struct snd_dbuf *b, unsigned int spd)
Definition: buffer.c:379
int chn_reset(struct pcm_channel *c, uint32_t fmt, uint32_t spd)
Definition: channel.c:1129
uint32_t snd_str2afmt(const char *req)
Definition: channel.c:1031
uint32_t snd_afmt2str(uint32_t afmt, char *buf, size_t len)
Definition: channel.c:1099
struct pcmchan_caps * chn_getcaps(struct pcm_channel *c)
Definition: channel.c:2277
int chn_notify(struct pcm_channel *c, u_int32_t flags)
Definition: channel.c:2302
int chn_abort(struct pcm_channel *c)
Definition: channel.c:927
int snd_fmtvalid(uint32_t fmt, uint32_t *fmtlist)
Definition: channel.c:982
u_int32_t chn_start(struct pcm_channel *c, int force)
Definition: channel.c:674
#define CHN_UNLOCK(c)
Definition: channel.h:322
#define PCMDIR_PLAY_VIRTUAL
Definition: channel.h:340
#define CHN_F_DIRTY
Definition: channel.h:365
#define CHN_LOCK(c)
Definition: channel.h:321
#define CHN_NAMELEN
Definition: channel.h:81
#define CHN_F_HAS_VCHAN
Definition: channel.h:370
#define CHN_F_PASSTHROUGH
Definition: channel.h:378
#define PCMDIR_PLAY
Definition: channel.h:339
#define CHN_F_VCHAN_ADAPTIVE
Definition: channel.h:373
#define PCMTRIG_START
Definition: channel.h:344
#define CHN_F_VCHAN_PASSTHROUGH
Definition: channel.h:372
#define CHN_EMPTY(x, y)
Definition: channel.h:178
#define CHN_N_TRIGGER
Definition: channel.h:416
#define CHN_INSERT_HEAD(x, y, z)
Definition: channel.h:187
#define CHN_FOREACH(x, y, z)
Definition: channel.h:181
#define CHN_F_BUSY
Definition: channel.h:364
#define CHN_LOCKASSERT(c)
Definition: channel.h:324
#define PCMDIR_REC_VIRTUAL
Definition: channel.h:342
#define CHN_INSERT_SORT_DESCEND(x, y, z)
Definition: channel.h:241
#define CHN_REMOVE(x, y, z)
Definition: channel.h:193
#define PCMTRIG_STOP
Definition: channel.h:347
#define CHN_F_VCHAN_DYNAMIC
Definition: channel.h:374
#define PCMDIR_REC
Definition: channel.h:341
#define CHN_STARTED(c)
Definition: channel.h:429
#define CHN_F_VIRTUAL
Definition: channel.h:376
#define AFMTSTR_LEN
Definition: channel.h:332
#define PCMTRIG_COMMON(x)
Definition: channel.h:350
#define PCMTRIG_ABORT
Definition: channel.h:348
struct pcm_channel * c
Definition: channel_if.m:106
METHOD int free
Definition: channel_if.m:110
struct snd_dbuf * b
Definition: channel_if.m:105
char * dsp_unit2name(char *buf, size_t len, int unit)
Definition: dsp.c:2534
int feeder_rate_max
Definition: feeder_rate.c:157
int feeder_rate_round
Definition: feeder_rate.c:158
struct pcmchan_matrix * feeder_matrix_format_map(uint32_t)
int feeder_rate_min
Definition: feeder_rate.c:156
int feeder_chain(struct pcm_channel *)
Definition: feeder_chain.c:581
int dir
Definition: hdac_if.m:45
bus_addr_t buf
Definition: hdac_if.m:63
uint8_t r
SYSCTL_INT(_hw_midi, OID_AUTO, debug, CTLFLAG_RW, &midi_debug, 0, "")
#define KOBJMETHOD_END
Definition: midi.c:76
unsigned dev
Definition: mixer_if.m:59
#define RANGE(var, low, high)
Definition: sequencer.h:45
int pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
Definition: sound.c:149
struct pcm_channel * pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
Definition: sound.c:483
devclass_t pcm_devclass
Definition: sound.c:49
int pcm_chn_destroy(struct pcm_channel *ch)
Definition: sound.c:596
int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
Definition: sound.c:618
int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
Definition: sound.c:650
#define SD_F_AUTOVCHAN
Definition: sound.h:133
#define PCM_RELEASE_QUICK(x)
Definition: sound.h:573
#define AFMT_VCHAN
Definition: sound.h:203
#define PCM_ACQUIRE(x)
Definition: sound.h:546
#define AFMT_PASSTHROUGH
Definition: sound.h:205
#define PCM_BUSYASSERT(x)
Definition: sound.h:580
#define PCM_WAIT(x)
Definition: sound.h:540
#define SND_MAXVCHANS
Definition: sound.h:130
#define PCM_UNLOCK(d)
Definition: sound.h:421
#define PCM_LOCK(d)
Definition: sound.h:420
#define PCM_UNLOCKASSERT(d)
Definition: sound.h:424
#define PCM_REGISTERED(x)
Definition: sound.h:178
u_int32_t flags
Definition: channel.h:96
kobj_t methods
Definition: channel.h:86
struct pcm_channel::@28 channels
struct pcm_channel::@27 children
struct pcm_channel::@27::@29 busy
u_int32_t speed
Definition: channel.h:94
void * devinfo
Definition: channel.h:106
struct snd_dbuf * bufhard
Definition: channel.h:103
int direction
Definition: channel.h:100
u_int32_t format
Definition: channel.h:95
struct pcm_channel * parentchannel
Definition: channel.h:105
int unit
Definition: channel.h:108
char comm[MAXCOMLEN+1]
Definition: channel.h:110
struct snddev_info * parentsnddev
Definition: channel.h:104
device_t dev
Definition: channel.h:107
u_int32_t maxspeed
Definition: channel.h:34
u_int32_t * fmtlist
Definition: channel.h:35
u_int32_t caps
Definition: channel.h:36
u_int32_t minspeed
Definition: channel.h:34
unsigned playcount
Definition: sound.h:395
unsigned rvchancount
Definition: sound.h:395
unsigned flags
Definition: sound.h:396
unsigned pvchancount
Definition: sound.h:395
uint32_t pvchanformat
Definition: sound.h:404
unsigned reccount
Definition: sound.h:395
uint32_t rvchanformat
Definition: sound.h:405
uint32_t rvchanrate
Definition: sound.h:405
struct sysctl_oid * play_sysctl_tree
Definition: sound.h:408
struct sysctl_ctx_list play_sysctl_ctx rec_sysctl_ctx
Definition: sound.h:407
struct sysctl_oid * rec_sysctl_tree
Definition: sound.h:408
uint32_t pvchanrate
Definition: sound.h:404
uint32_t fmtlist[FMTLIST_MAX]
Definition: vchan.c:58
struct pcmchan_caps caps
Definition: vchan.c:57
int trigger
Definition: vchan.c:59
struct pcm_channel * channel
Definition: vchan.c:56
const void * req
int vchan_destroy(struct pcm_channel *c)
Definition: vchan.c:852
static int vchan_free(kobj_t obj, void *data)
Definition: vchan.c:102
#define FMTLIST_MAX
Definition: vchan.c:44
#define FMTLIST_OFFSET
Definition: vchan.c:45
static struct pcmchan_caps * vchan_getcaps(kobj_t obj, void *data)
Definition: vchan.c:188
#define DIGFMTS_MAX
Definition: vchan.c:46
static uint32_t vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
Definition: vchan.c:126
static int sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
Definition: vchan.c:445
static void pcm_getparentchannel(struct snddev_info *d, struct pcm_channel **wrch, struct pcm_channel **rdch)
Definition: vchan.c:254
static void * vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
Definition: vchan.c:63
static struct pcmchan_matrix * vchan_getmatrix(kobj_t obj, void *data, uint32_t format)
Definition: vchan.c:235
static int sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
Definition: vchan.c:553
int vchan_sync(struct pcm_channel *c)
Definition: vchan.c:906
SND_DECLARE_FILE("$FreeBSD$")
int vchan_create(struct pcm_channel *parent, int num)
Definition: vchan.c:667
static int vchan_trigger(kobj_t obj, void *data, int go)
Definition: vchan.c:138
#define VCHAN_SPD_HINT(x)
Definition: vchan.c:663
static int sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
Definition: vchan.c:351
#define VCHAN_FMT_HINT(x)
Definition: vchan.c:661
static kobj_method_t vchan_methods[]
Definition: vchan.c:241
CHANNEL_DECLARE(vchan)
static int sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
Definition: vchan.c:296
static int vchan_setformat(kobj_t obj, void *data, uint32_t format)
Definition: vchan.c:111
void vchan_initsys(device_t dev)
Definition: vchan.c:940
#define VCHAN_ACCESSIBLE(c)
Definition: vchan.c:440
#define VCHAN_DEFAULT_FORMAT
Definition: vchan.h:55
#define VCHAN_DEFAULT_RATE
Definition: vchan.h:56
#define VCHAN_SYSCTL_DATA(x, y)
Definition: vchan.h:64
#define VCHAN_PLAY
Definition: vchan.h:58
#define VCHAN_SYSCTL_DIR(x)
Definition: vchan.h:70
#define VCHAN_SYSCTL_DATA_SIZE
Definition: vchan.h:68
#define VCHAN_REC
Definition: vchan.h:59
#define VCHAN_SYNC_REQUIRED(c)
Definition: vchan.h:45
#define VCHAN_SYSCTL_UNIT(x)
Definition: vchan.h:69