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
39 SND_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
49 static int snd_passthrough_verbose = 0;
50 SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN,
51 &snd_passthrough_verbose, 0, "passthrough verbosity");
52
53 #endif
54
55 struct vchan_info {
56 struct pcm_channel *channel;
57 struct pcmchan_caps caps;
58 uint32_t fmtlist[FMTLIST_MAX];
59 int trigger;
60 };
61
62 static void *
63 vchan_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
90 info->fmtlist[j] = VCHAN_DEFAULT_FORMAT;
91 info->caps.fmtlist = info->fmtlist +
92 ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET);
93
94 CHN_UNLOCK(p);
95
96 c->flags |= CHN_F_VIRTUAL;
97
98 return (info);
99 }
100
101 static int
102 vchan_free(kobj_t obj, void *data)
103 {
104
105 free(data, M_DEVBUF);
106
107 return (0);
108 }
109
110 static int
111 vchan_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
125 static uint32_t
126 vchan_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
137 static int
138 vchan_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
154 CHN_LOCKASSERT(c);
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
187 static struct pcmchan_caps *
188 vchan_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
200 CHN_LOCKASSERT(c);
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 }
212 if (c->format & AFMT_PASSTHROUGH)
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);
225 info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT;
226 }
227 info->caps.minspeed = pspeed;
228 info->caps.maxspeed = info->caps.minspeed;
229 }
230
231 return (&info->caps);
232 }
233
234 static struct pcmchan_matrix *
235 vchan_getmatrix(kobj_t obj, void *data, uint32_t format)
236 {
237
238 return (feeder_matrix_format_map(format));
239 }
240
241 static 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),
249 KOBJMETHOD_END
250 };
251 CHANNEL_DECLARE(vchan);
252
253 static void
254 pcm_getparentchannel(struct snddev_info *d,
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
261 PCM_BUSYASSERT(d);
262 PCM_UNLOCKASSERT(d);
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
295 static int
296 sysctl_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
345 PCM_RELEASE_QUICK(d);
346
347 return err;
348 }
349
350 static int
351 sysctl_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:
368 direction = PCMDIR_PLAY;
369 break;
370 case VCHAN_REC:
371 direction = PCMDIR_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) {
388 PCM_RELEASE_QUICK(d);
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);
396 if (c->flags & CHN_F_VCHAN_PASSTHROUGH)
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)
408 dflags = CHN_F_VCHAN_PASSTHROUGH;
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)
414 dflags = 0;
415 else {
416 PCM_RELEASE_QUICK(d);
417 return (EINVAL);
418 }
419 CHN_LOCK(c);
420 if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) ||
421 (c->flags & CHN_F_PASSTHROUGH)) {
422 CHN_UNLOCK(c);
423 PCM_RELEASE_QUICK(d);
424 return (0);
425 }
426 c->flags &= ~CHN_F_VCHAN_DYNAMIC;
427 c->flags |= dflags;
428 CHN_UNLOCK(c);
429 }
430
431 PCM_RELEASE_QUICK(d);
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)))
444 static int
445 sysctl_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) {
490 PCM_RELEASE_QUICK(d);
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) {
503 PCM_RELEASE_QUICK(d);
504 return (ret);
505 }
506
507 if (newspd < 1 || newspd < feeder_rate_min ||
508 newspd > feeder_rate_max) {
509 PCM_RELEASE_QUICK(d);
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
547 PCM_RELEASE_QUICK(d);
548
549 return (ret);
550 }
551
552 static int
553 sysctl_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:
570 direction = PCMDIR_PLAY;
571 vchancount = d->pvchancount;
572 vchanformat = &d->pvchanformat;
573 break;
574 case VCHAN_REC:
575 direction = PCMDIR_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) {
599 PCM_RELEASE_QUICK(d);
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) {
617 PCM_RELEASE_QUICK(d);
618 return (ret);
619 }
620
621 newfmt = snd_str2afmt(fmtstr);
622 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) {
623 PCM_RELEASE_QUICK(d);
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
654 PCM_RELEASE_QUICK(d);
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
666 int
667 vchan_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
677 PCM_BUSYASSERT(d);
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;
832 parent->flags |= CHN_F_VCHAN_PASSTHROUGH;
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
851 int
852 vchan_destroy(struct pcm_channel *c)
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
862 CHN_LOCKASSERT(c);
863
864 d = c->parentsnddev;
865 parent = c->parentchannel;
866
867 PCM_BUSYASSERT(d);
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
902 int
903 #ifdef SND_DEBUG
904 vchan_passthrough(struct pcm_channel *c, const char *caller)
905 #else
906 vchan_sync(struct pcm_channel *c)
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__));
914 CHN_LOCKASSERT(c);
915 CHN_LOCKASSERT(c->parentchannel);
916
917 sndbuf_setspd(c->bufhard, c->parentchannel->speed);
918 c->flags |= CHN_F_PASSTHROUGH;
919 ret = feeder_chain(c);
920 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH);
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
939 void
940 vchan_initsys(device_t dev)
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,
952 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
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,
958 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
959 sysctl_dev_pcm_vchanmode, "A",
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,
965 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
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,
971 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
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,
978 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
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,
984 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
985 sysctl_dev_pcm_vchanmode, "A",
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,
991 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
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,
997 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
998 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
999 }
Cache object: 90d2ca60f0c03a96422c82f10223b399
|