2 * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
3 * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 /* Almost entirely rewritten to add multi-format/channels mixing support. */
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/vchan.h>
32 #include "feeder_if.h"
34 SND_DECLARE_FILE("$FreeBSD$");
36 MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder");
38 typedef uint32_t (*feed_vchan_mixer)(uint8_t *, uint8_t *, uint32_t);
41 struct pcm_channel *channel;
42 struct pcmchan_caps caps;
47 /* support everything (mono / stereo), except a-law / mu-law */
48 static struct afmtstr_table vchan_supported_fmts[] = {
49 { "u8", AFMT_U8 }, { "s8", AFMT_S8 },
50 { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE },
51 { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE },
52 { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE },
53 { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE },
54 { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE },
55 { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE },
59 /* alias table, shorter. */
62 } vchan_fmtstralias[] = {
63 { "8", "u8" }, { "16", "s16le" },
64 { "24", "s24le" }, { "32", "s32le" },
68 #define vchan_valid_format(fmt) \
69 afmt2afmtstr(vchan_supported_fmts, fmt, NULL, 0, 0, \
70 AFMTSTR_STEREO_RETURN)
71 #define vchan_valid_strformat(strfmt) \
72 afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN);
75 * Need specialized WRITE macros since 32bit might involved saturation
76 * if calculation is done within 32bit arithmetic.
78 #define VCHAN_PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val)
79 #define VCHAN_PCM_WRITE_S16_LE(b8, val) PCM_WRITE_S16_LE(b8, val)
80 #define VCHAN_PCM_WRITE_S24_LE(b8, val) PCM_WRITE_S24_LE(b8, val)
81 #define VCHAN_PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val)
82 #define VCHAN_PCM_WRITE_S16_BE(b8, val) PCM_WRITE_S16_BE(b8, val)
83 #define VCHAN_PCM_WRITE_S24_BE(b8, val) PCM_WRITE_S24_BE(b8, val)
84 #define VCHAN_PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val)
85 #define VCHAN_PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val)
86 #define VCHAN_PCM_WRITE_U16_LE(b8, val) PCM_WRITE_U16_LE(b8, val)
87 #define VCHAN_PCM_WRITE_U24_LE(b8, val) PCM_WRITE_U24_LE(b8, val)
88 #define VCHAN_PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val)
89 #define VCHAN_PCM_WRITE_U16_BE(b8, val) PCM_WRITE_U16_BE(b8, val)
90 #define VCHAN_PCM_WRITE_U24_BE(b8, val) PCM_WRITE_U24_BE(b8, val)
91 #define VCHAN_PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val)
93 #define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \
95 feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(uint8_t *to, uint8_t *tmp, \
107 tmp -= PCM_##FMTBIT##_BPS; \
108 to -= PCM_##FMTBIT##_BPS; \
109 i -= PCM_##FMTBIT##_BPS; \
110 x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp); \
111 y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to); \
112 z = (VCHAN_INTCAST)x + y; \
113 x = PCM_CLAMP_##SIGN##FMTBIT(z); \
114 VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x); \
120 FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne)
121 FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le)
122 FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le)
123 FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le)
124 FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be)
125 FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be)
126 FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be)
127 FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne)
128 FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le)
129 FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le)
130 FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le)
131 FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be)
132 FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be)
133 FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be)
135 struct feed_vchan_info {
138 feed_vchan_mixer mix;
141 static struct feed_vchan_info feed_vchan_info_tbl[] = {
142 { AFMT_S8, PCM_8_BPS, feed_vchan_mix_s8ne },
143 { AFMT_S16_LE, PCM_16_BPS, feed_vchan_mix_s16le },
144 { AFMT_S24_LE, PCM_24_BPS, feed_vchan_mix_s24le },
145 { AFMT_S32_LE, PCM_32_BPS, feed_vchan_mix_s32le },
146 { AFMT_S16_BE, PCM_16_BPS, feed_vchan_mix_s16be },
147 { AFMT_S24_BE, PCM_24_BPS, feed_vchan_mix_s24be },
148 { AFMT_S32_BE, PCM_32_BPS, feed_vchan_mix_s32be },
149 { AFMT_U8, PCM_8_BPS, feed_vchan_mix_u8ne },
150 { AFMT_U16_LE, PCM_16_BPS, feed_vchan_mix_u16le },
151 { AFMT_U24_LE, PCM_24_BPS, feed_vchan_mix_u24le },
152 { AFMT_U32_LE, PCM_32_BPS, feed_vchan_mix_u32le },
153 { AFMT_U16_BE, PCM_16_BPS, feed_vchan_mix_u16be },
154 { AFMT_U24_BE, PCM_24_BPS, feed_vchan_mix_u24be },
155 { AFMT_U32_BE, PCM_32_BPS, feed_vchan_mix_u32be },
158 #define FVCHAN_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf)))
159 #define FVCHAN_INFOIDX(m) (((m) >> 4) & 0x1f)
160 #define FVCHAN_CHANNELS(m) ((m) & 0xf)
163 feed_vchan_init(struct pcm_feeder *f)
167 if (f->desc->out != f->desc->in)
170 channels = (f->desc->out & AFMT_STEREO) ? 2 : 1;
172 for (i = 0; i < sizeof(feed_vchan_info_tbl) /
173 sizeof(feed_vchan_info_tbl[0]); i++) {
174 if ((f->desc->out & ~AFMT_STEREO) ==
175 feed_vchan_info_tbl[i].format) {
176 f->data = (void *)FVCHAN_DATA(i, channels);
185 feed_vchan_rec(struct pcm_channel *c)
187 struct pcm_channel *ch;
188 struct snd_dbuf *b, *bs;
192 * Reset ready and moving pointer. We're not using bufsoft
193 * anywhere since its sole purpose is to become the primary
194 * distributor for the recorded buffer and also as an interrupt
195 * threshold progress indicator.
200 cnt = sndbuf_getsize(b);
203 cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf, cnt,
206 sndbuf_acquire(b, b->tmpbuf, cnt);
207 cnt = sndbuf_getfree(b);
211 /* Not enough data */
212 if (b->rl < sndbuf_getbps(b)) {
218 * Keep track of ready and moving pointer since we will use
219 * bufsoft over and over again, pretending nothing has happened.
223 CHN_FOREACH(ch, c, children.busy) {
225 if (!(ch->flags & CHN_F_TRIGGERED)) {
230 if (ch->flags & CHN_F_MAPPED)
231 sndbuf_dispose(bs, NULL, sndbuf_getready(bs));
232 cnt = sndbuf_getfree(bs);
233 if (cnt < sndbuf_getbps(bs)) {
238 cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, cnt, b);
240 sndbuf_acquire(bs, bs->tmpbuf, cnt);
241 cnt = sndbuf_getfree(bs);
245 * Not entirely flushed out...
251 * Rewind buffer position for next virtual channel.
258 * Set ready pointer to indicate that our children are ready
259 * to be woken up, also as an interrupt threshold progress
265 * Return 0 to bail out early from sndbuf_feed() loop.
266 * No need to increase feedcount counter since part of this
267 * feeder chains already include feed_root().
273 feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
274 uint32_t count, void *source)
276 struct feed_vchan_info *info;
277 struct snd_dbuf *src = source;
278 struct pcm_channel *ch;
279 uint32_t cnt, mcnt, rcnt, sz;
282 if (c->direction == PCMDIR_REC)
283 return (feed_vchan_rec(c));
285 sz = sndbuf_getsize(src);
289 info = &feed_vchan_info_tbl[FVCHAN_INFOIDX((intptr_t)f->data)];
290 sz = info->bps * FVCHAN_CHANNELS((intptr_t)f->data);
296 * we are going to use our source as a temporary buffer since it's
297 * got no other purpose. we obtain our data by traversing the channel
298 * list of children and calling vchan_mix_* to mix count bytes from
299 * each into our destination buffer, b
301 tmp = sndbuf_getbuf(src);
305 CHN_FOREACH(ch, c, children.busy) {
307 if (!(ch->flags & CHN_F_TRIGGERED)) {
311 if ((ch->flags & CHN_F_MAPPED) && !(ch->flags & CHN_F_CLOSING))
312 sndbuf_acquire(ch->bufsoft, NULL,
313 sndbuf_getfree(ch->bufsoft));
315 rcnt = FEEDER_FEED(ch->feeder, ch, b, count,
320 cnt = FEEDER_FEED(ch->feeder, ch, tmp, count,
326 sndbuf_zerodata(f->desc->out),
330 cnt = info->mix(b, tmp, cnt);
338 if (++c->feedcount == 0)
344 static struct pcm_feederdesc feeder_vchan_desc[] = {
345 {FEEDER_MIXER, AFMT_S8, AFMT_S8, 0},
346 {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0},
347 {FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0},
348 {FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0},
349 {FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0},
350 {FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0},
351 {FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0},
352 {FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
353 {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
354 {FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
355 {FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
356 {FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
357 {FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
358 {FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
359 {FEEDER_MIXER, AFMT_U8, AFMT_U8, 0},
360 {FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0},
361 {FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0},
362 {FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0},
363 {FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0},
364 {FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0},
365 {FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0},
366 {FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
367 {FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
368 {FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
369 {FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
370 {FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
371 {FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
372 {FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
375 static kobj_method_t feeder_vchan_methods[] = {
376 KOBJMETHOD(feeder_init, feed_vchan_init),
377 KOBJMETHOD(feeder_feed, feed_vchan),
380 FEEDER_DECLARE(feeder_vchan, 2, NULL);
382 /************************************************************/
385 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
386 struct pcm_channel *c, int dir)
390 KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC,
391 ("vchan_init: bad direction"));
392 KASSERT(c != NULL && c->parentchannel != NULL,
393 ("vchan_init: bad channels"));
395 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
397 ch->trigger = PCMTRIG_STOP;
399 c->flags |= CHN_F_VIRTUAL;
405 vchan_free(kobj_t obj, void *data)
407 free(data, M_DEVBUF);
413 vchan_setformat(kobj_t obj, void *data, uint32_t format)
415 struct vchinfo *ch = data;
417 if (fmtvalid(format, ch->fmtlist) == 0)
424 vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
426 struct vchinfo *ch = data;
427 struct pcm_channel *p = ch->channel->parentchannel;
429 return (sndbuf_getspd(p->bufsoft));
433 vchan_trigger(kobj_t obj, void *data, int go)
435 struct vchinfo *ch = data;
436 struct pcm_channel *c, *p;
439 if (!PCMTRIG_COMMON(go) || go == ch->trigger)
443 p = c->parentchannel;
444 otrigger = ch->trigger;
452 if (otrigger != PCMTRIG_START) {
453 CHN_INSERT_HEAD(p, c, children.busy);
458 if (otrigger == PCMTRIG_START) {
459 CHN_REMOVE(p, c, children.busy);
466 err = chn_notify(p, CHN_N_TRIGGER);
473 static struct pcmchan_caps *
474 vchan_getcaps(kobj_t obj, void *data)
476 struct vchinfo *ch = data;
477 struct pcm_channel *c, *p;
481 p = c->parentchannel;
482 ch->caps.minspeed = sndbuf_getspd(p->bufsoft);
483 ch->caps.maxspeed = ch->caps.minspeed;
486 fmt = sndbuf_getfmt(p->bufsoft);
487 if (fmt != vchan_valid_format(fmt)) {
488 device_printf(c->dev,
489 "%s: WARNING: invalid vchan format! (0x%08x)\n",
491 fmt = VCHAN_DEFAULT_AFMT;
493 ch->fmtlist[0] = fmt;
494 ch->caps.fmtlist = ch->fmtlist;
499 static kobj_method_t vchan_methods[] = {
500 KOBJMETHOD(channel_init, vchan_init),
501 KOBJMETHOD(channel_free, vchan_free),
502 KOBJMETHOD(channel_setformat, vchan_setformat),
503 KOBJMETHOD(channel_setspeed, vchan_setspeed),
504 KOBJMETHOD(channel_trigger, vchan_trigger),
505 KOBJMETHOD(channel_getcaps, vchan_getcaps),
508 CHANNEL_DECLARE(vchan);
511 * On the fly vchan rate settings
515 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
517 struct snddev_info *d;
518 struct pcm_channel *c, *ch = NULL;
519 struct pcmchan_caps *caps;
520 int *vchanrate, vchancount, direction, err, newspd;
522 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
523 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
529 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
531 direction = PCMDIR_PLAY;
532 vchancount = d->pvchancount;
533 vchanrate = &d->pvchanrate;
536 direction = PCMDIR_REC;
537 vchancount = d->rvchancount;
538 vchanrate = &d->rvchanrate;
546 if (vchancount < 1) {
556 CHN_FOREACH(c, d, channels.pcm) {
558 if (c->direction == direction) {
559 if (c->flags & CHN_F_VIRTUAL) {
561 if (ch != NULL && ch != c->parentchannel) {
563 PCM_RELEASE_QUICK(d);
566 } else if (c->flags & CHN_F_HAS_VCHAN) {
570 PCM_RELEASE_QUICK(d);
580 PCM_RELEASE_QUICK(d);
584 err = sysctl_handle_int(oidp, &newspd, 0, req);
585 if (err == 0 && req->newptr != NULL) {
586 if (newspd < 1 || newspd < feeder_rate_min ||
587 newspd > feeder_rate_max) {
588 PCM_RELEASE_QUICK(d);
592 if (feeder_rate_round) {
593 caps = chn_getcaps(ch);
594 if (caps == NULL || newspd < caps->minspeed ||
595 newspd > caps->maxspeed) {
597 PCM_RELEASE_QUICK(d);
601 if (CHN_STOPPED(ch) && newspd != ch->speed) {
602 err = chn_setspeed(ch, newspd);
604 * Try to avoid FEEDER_RATE on parent channel if the
605 * requested value is not supported by the hardware.
607 if (!err && feeder_rate_round &&
608 (ch->feederflags & (1 << FEEDER_RATE))) {
609 newspd = sndbuf_getspd(ch->bufhard);
610 err = chn_setspeed(ch, newspd);
618 PCM_RELEASE_QUICK(d);
624 sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
626 struct snddev_info *d;
627 struct pcm_channel *c, *ch = NULL;
628 uint32_t newfmt, spd;
629 int *vchanformat, vchancount, direction, err, i;
630 char fmtstr[AFMTSTR_MAXSZ];
632 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
633 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
639 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
641 direction = PCMDIR_PLAY;
642 vchancount = d->pvchancount;
643 vchanformat = &d->pvchanformat;
646 direction = PCMDIR_REC;
647 vchancount = d->rvchancount;
648 vchanformat = &d->rvchanformat;
656 if (vchancount < 1) {
664 CHN_FOREACH(c, d, channels.pcm) {
666 if (c->direction == direction) {
667 if (c->flags & CHN_F_VIRTUAL) {
669 if (ch != NULL && ch != c->parentchannel) {
671 PCM_RELEASE_QUICK(d);
674 } else if (c->flags & CHN_F_HAS_VCHAN) {
678 PCM_RELEASE_QUICK(d);
683 afmt2afmtstr(vchan_supported_fmts,
684 ch->format, fmtstr, sizeof(fmtstr),
685 AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) {
686 strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT,
694 PCM_RELEASE_QUICK(d);
698 err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
699 if (err == 0 && req->newptr != NULL) {
700 for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) {
701 if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) {
702 strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr,
707 newfmt = vchan_valid_strformat(fmtstr);
709 PCM_RELEASE_QUICK(d);
713 if (CHN_STOPPED(ch) && newfmt != ch->format) {
714 /* Get channel speed, before chn_reset() screw it. */
716 err = chn_reset(ch, newfmt);
718 err = chn_setspeed(ch, spd);
720 *vchanformat = newfmt;
725 PCM_RELEASE_QUICK(d);
731 /* virtual channel interface */
733 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
734 "play.vchanformat" : "rec.vchanformat"
735 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \
736 "play.vchanrate" : "rec.vchanrate"
739 vchan_create(struct pcm_channel *parent, int num)
741 struct snddev_info *d = parent->parentsnddev;
742 struct pcm_channel *ch, *tmp, *after;
743 struct pcmchan_caps *parent_caps;
745 int err, first, speed, r;
750 if (!(parent->flags & CHN_F_BUSY))
753 if (parent->direction == PCMDIR_PLAY) {
754 direction = PCMDIR_PLAY_VIRTUAL;
755 vchanfmt = d->pvchanformat;
756 speed = d->pvchanrate;
757 } else if (parent->direction == PCMDIR_REC) {
758 direction = PCMDIR_REC_VIRTUAL;
759 vchanfmt = d->rvchanformat;
760 speed = d->rvchanrate;
765 /* create a new playback channel */
767 ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent);
774 /* add us to our grandparent's channel list */
775 err = pcm_chn_add(d, ch);
784 /* add us to our parent channel's children */
785 first = CHN_EMPTY(parent, children);
787 CHN_FOREACH(tmp, parent, children) {
788 if (CHN_CHAN(tmp) > CHN_CHAN(ch))
790 else if (CHN_CHAN(tmp) < CHN_CHAN(ch))
794 CHN_INSERT_AFTER(after, ch, children);
796 CHN_INSERT_HEAD(parent, ch, children);
798 parent->flags |= CHN_F_HAS_VCHAN;
801 parent_caps = chn_getcaps(parent);
802 if (parent_caps == NULL)
810 r = resource_string_value(
811 device_get_name(parent->dev),
812 device_get_unit(parent->dev),
813 VCHAN_FMT_HINT(direction),
819 vchanfmt = vchan_valid_strformat(vfmt);
820 for (r = 0; vchanfmt == 0 &&
821 vchan_fmtstralias[r].alias != NULL;
823 if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) {
824 vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr);
830 vchanfmt = VCHAN_DEFAULT_AFMT;
832 err = chn_reset(parent, vchanfmt);
837 * This is very sad. Few soundcards advertised as being
838 * able to do (insanely) higher/lower speed, but in
839 * reality, they simply can't. At least, we give user chance
840 * to set sane value via kernel hints or sysctl.
844 r = resource_int_value(
845 device_get_name(parent->dev),
846 device_get_unit(parent->dev),
847 VCHAN_SPD_HINT(direction),
852 * No saved value, no hint, NOTHING.
854 * Workaround for sb16 running
855 * poorly at 45k / 49k.
857 switch (parent_caps->maxspeed) {
863 speed = VCHAN_DEFAULT_SPEED;
864 if (speed > parent_caps->maxspeed)
865 speed = parent_caps->maxspeed;
868 if (speed < parent_caps->minspeed)
869 speed = parent_caps->minspeed;
873 if (feeder_rate_round) {
875 * Limit speed based on driver caps.
876 * This is supposed to help fixed rate, non-VRA
877 * AC97 cards, but.. (see below)
879 if (speed < parent_caps->minspeed)
880 speed = parent_caps->minspeed;
881 if (speed > parent_caps->maxspeed)
882 speed = parent_caps->maxspeed;
886 * We still need to limit the speed between
887 * feeder_rate_min <-> feeder_rate_max. This is
888 * just an escape goat if all of the above failed
891 if (speed < feeder_rate_min)
892 speed = feeder_rate_min;
893 if (speed > feeder_rate_max)
894 speed = feeder_rate_max;
896 err = chn_setspeed(parent, speed);
898 * Try to avoid FEEDER_RATE on parent channel if the
899 * requested value is not supported by the hardware.
901 if (!err && feeder_rate_round &&
902 (parent->feederflags & (1 << FEEDER_RATE))) {
903 speed = sndbuf_getspd(parent->bufhard);
904 err = chn_setspeed(parent, speed);
912 if (direction == PCMDIR_PLAY_VIRTUAL) {
913 d->pvchanformat = vchanfmt;
914 d->pvchanrate = speed;
916 d->rvchanformat = vchanfmt;
917 d->rvchanrate = speed;
924 CHN_REMOVE(parent, ch, children);
925 parent->flags &= ~CHN_F_HAS_VCHAN;
928 if (pcm_chn_remove(d, ch) == 0) {
942 vchan_destroy(struct pcm_channel *c)
944 struct pcm_channel *parent = c->parentchannel;
945 struct snddev_info *d = parent->parentsnddev;
952 if (!(parent->flags & CHN_F_BUSY)) {
956 if (CHN_EMPTY(parent, children)) {
961 /* remove us from our parent's children list */
962 CHN_REMOVE(parent, c, children);
964 if (CHN_EMPTY(parent, children)) {
965 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
967 if (chn_reset(parent, parent->format) == 0)
968 chn_setspeed(parent, spd);
973 /* remove us from our grandparent's channel list */
975 err = pcm_chn_remove(d, c);
978 /* destroy ourselves */
980 err = pcm_chn_destroy(c);
986 vchan_initsys(device_t dev)
989 struct snddev_info *d;
992 unit = device_get_unit(dev);
993 d = device_get_softc(dev);
996 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
997 SYSCTL_CHILDREN(d->play_sysctl_tree),
998 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW,
999 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1000 sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
1001 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1002 SYSCTL_CHILDREN(d->play_sysctl_tree),
1003 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW,
1004 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1005 sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate");
1006 SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1007 SYSCTL_CHILDREN(d->play_sysctl_tree),
1008 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW,
1009 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1010 sysctl_hw_snd_vchanformat, "A", "virtual channel format");
1012 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1013 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1014 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW,
1015 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1016 sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
1017 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1018 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1019 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW,
1020 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1021 sysctl_hw_snd_vchanrate, "I", "virtual channel base speed/rate");
1022 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1023 SYSCTL_CHILDREN(d->rec_sysctl_tree),
1024 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW,
1025 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1026 sysctl_hw_snd_vchanformat, "A", "virtual channel format");