]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sound/pcm/vchan.c
Mark more nodes as CTLFLAG_MPSAFE or CTLFLAG_NEEDGIANT (17 of many)
[FreeBSD/FreeBSD.git] / sys / dev / sound / pcm / vchan.c
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") == 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 }