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