1098ca2bdSWarner Losh /*- 23f225978SCameron Grant * Copyright (c) 2001 Cameron Grant <cg@freebsd.org> 3285648f9SCameron Grant * All rights reserved. 4285648f9SCameron Grant * 5285648f9SCameron Grant * Redistribution and use in source and binary forms, with or without 6285648f9SCameron Grant * modification, are permitted provided that the following conditions 7285648f9SCameron Grant * are met: 8285648f9SCameron Grant * 1. Redistributions of source code must retain the above copyright 9285648f9SCameron Grant * notice, this list of conditions and the following disclaimer. 10285648f9SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 11285648f9SCameron Grant * notice, this list of conditions and the following disclaimer in the 12285648f9SCameron Grant * documentation and/or other materials provided with the distribution. 13285648f9SCameron Grant * 14285648f9SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15285648f9SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16285648f9SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17285648f9SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18285648f9SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19285648f9SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20285648f9SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21285648f9SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22285648f9SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23285648f9SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24285648f9SCameron Grant * SUCH DAMAGE. 25285648f9SCameron Grant */ 26285648f9SCameron Grant 27285648f9SCameron Grant #include <dev/sound/pcm/sound.h> 28285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 29285648f9SCameron Grant #include "feeder_if.h" 30285648f9SCameron Grant 3167b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3267b1dce3SCameron Grant 3387506547SAlexander Leidinger /* 3487506547SAlexander Leidinger * Default speed 3587506547SAlexander Leidinger */ 3687506547SAlexander Leidinger #define VCHAN_DEFAULT_SPEED 48000 3787506547SAlexander Leidinger 3887506547SAlexander Leidinger extern int feeder_rate_ratemin; 3987506547SAlexander Leidinger extern int feeder_rate_ratemax; 4087506547SAlexander Leidinger 41285648f9SCameron Grant struct vchinfo { 4249c5e6e2SCameron Grant u_int32_t spd, fmt, blksz, bps, run; 43285648f9SCameron Grant struct pcm_channel *channel, *parent; 44285648f9SCameron Grant struct pcmchan_caps caps; 45285648f9SCameron Grant }; 46285648f9SCameron Grant 47285648f9SCameron Grant static u_int32_t vchan_fmt[] = { 48285648f9SCameron Grant AFMT_STEREO | AFMT_S16_LE, 49285648f9SCameron Grant 0 50285648f9SCameron Grant }; 51285648f9SCameron Grant 52285648f9SCameron Grant static int 53285648f9SCameron Grant vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count) 54285648f9SCameron Grant { 55285648f9SCameron Grant /* 56285648f9SCameron Grant * to is the output buffer, tmp is the input buffer 57285648f9SCameron Grant * count is the number of 16bit samples to mix 58285648f9SCameron Grant */ 59285648f9SCameron Grant int i; 60285648f9SCameron Grant int x; 61285648f9SCameron Grant 62285648f9SCameron Grant for(i = 0; i < count; i++) { 63285648f9SCameron Grant x = to[i]; 64285648f9SCameron Grant x += tmp[i]; 65285648f9SCameron Grant if (x < -32768) { 66285648f9SCameron Grant /* printf("%d + %d = %d (u)\n", to[i], tmp[i], x); */ 67285648f9SCameron Grant x = -32768; 68285648f9SCameron Grant } 69285648f9SCameron Grant if (x > 32767) { 70285648f9SCameron Grant /* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */ 71285648f9SCameron Grant x = 32767; 72285648f9SCameron Grant } 73285648f9SCameron Grant to[i] = x & 0x0000ffff; 74285648f9SCameron Grant } 75285648f9SCameron Grant return 0; 76285648f9SCameron Grant } 77285648f9SCameron Grant 78285648f9SCameron Grant static int 79285648f9SCameron Grant feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) 80285648f9SCameron Grant { 81285648f9SCameron Grant /* we're going to abuse things a bit */ 82285648f9SCameron Grant struct snd_dbuf *src = source; 83285648f9SCameron Grant struct pcmchan_children *cce; 84285648f9SCameron Grant struct pcm_channel *ch; 8597d69a96SAlexander Leidinger uint32_t sz; 86285648f9SCameron Grant int16_t *tmp, *dst; 8797d69a96SAlexander Leidinger unsigned int cnt, rcnt = 0; 88285648f9SCameron Grant 8997d69a96SAlexander Leidinger #if 0 9012e524a2SDon Lewis if (sndbuf_getsize(src) < count) 9112e524a2SDon Lewis panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x", 9212e524a2SDon Lewis c->name, sndbuf_getsize(src), count, c->flags); 9397d69a96SAlexander Leidinger #endif 9497d69a96SAlexander Leidinger sz = sndbuf_getsize(src); 9597d69a96SAlexander Leidinger if (sz < count) 9697d69a96SAlexander Leidinger count = sz; 97285648f9SCameron Grant count &= ~1; 9897d69a96SAlexander Leidinger if (count < 2) 9997d69a96SAlexander Leidinger return 0; 100285648f9SCameron Grant bzero(b, count); 101285648f9SCameron Grant 102285648f9SCameron Grant /* 103285648f9SCameron Grant * we are going to use our source as a temporary buffer since it's 104285648f9SCameron Grant * got no other purpose. we obtain our data by traversing the channel 105285648f9SCameron Grant * list of children and calling vchan_mix_* to mix count bytes from each 106285648f9SCameron Grant * into our destination buffer, b 107285648f9SCameron Grant */ 108285648f9SCameron Grant dst = (int16_t *)b; 109285648f9SCameron Grant tmp = (int16_t *)sndbuf_getbuf(src); 110285648f9SCameron Grant bzero(tmp, count); 111285648f9SCameron Grant SLIST_FOREACH(cce, &c->children, link) { 112285648f9SCameron Grant ch = cce->channel; 11312e524a2SDon Lewis CHN_LOCK(ch); 11449c5e6e2SCameron Grant if (ch->flags & CHN_F_TRIGGERED) { 115193d5719SCameron Grant if (ch->flags & CHN_F_MAPPED) 116193d5719SCameron Grant sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft)); 117285648f9SCameron Grant cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft); 11897d69a96SAlexander Leidinger vchan_mix_s16(dst, tmp, cnt >> 1); 11997d69a96SAlexander Leidinger if (cnt > rcnt) 12097d69a96SAlexander Leidinger rcnt = cnt; 121285648f9SCameron Grant } 12212e524a2SDon Lewis CHN_UNLOCK(ch); 12349c5e6e2SCameron Grant } 124285648f9SCameron Grant 12597d69a96SAlexander Leidinger return rcnt & ~1; 126285648f9SCameron Grant } 127285648f9SCameron Grant 128285648f9SCameron Grant static struct pcm_feederdesc feeder_vchan_s16_desc[] = { 129285648f9SCameron Grant {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 130285648f9SCameron Grant {0}, 131285648f9SCameron Grant }; 132285648f9SCameron Grant static kobj_method_t feeder_vchan_s16_methods[] = { 133285648f9SCameron Grant KOBJMETHOD(feeder_feed, feed_vchan_s16), 134285648f9SCameron Grant { 0, 0 } 135285648f9SCameron Grant }; 136285648f9SCameron Grant FEEDER_DECLARE(feeder_vchan_s16, 2, NULL); 137285648f9SCameron Grant 138285648f9SCameron Grant /************************************************************/ 139285648f9SCameron Grant 140285648f9SCameron Grant static void * 141285648f9SCameron Grant vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 142285648f9SCameron Grant { 143285648f9SCameron Grant struct vchinfo *ch; 144285648f9SCameron Grant struct pcm_channel *parent = devinfo; 145285648f9SCameron Grant 146285648f9SCameron Grant KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction")); 147a163d034SWarner Losh ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 14897d69a96SAlexander Leidinger if (!ch) 14997d69a96SAlexander Leidinger return NULL; 150285648f9SCameron Grant ch->parent = parent; 151285648f9SCameron Grant ch->channel = c; 152285648f9SCameron Grant ch->fmt = AFMT_U8; 153285648f9SCameron Grant ch->spd = DSP_DEFAULT_SPEED; 154285648f9SCameron Grant ch->blksz = 2048; 155285648f9SCameron Grant 156285648f9SCameron Grant c->flags |= CHN_F_VIRTUAL; 157285648f9SCameron Grant 158285648f9SCameron Grant return ch; 159285648f9SCameron Grant } 160285648f9SCameron Grant 161285648f9SCameron Grant static int 162285648f9SCameron Grant vchan_free(kobj_t obj, void *data) 163285648f9SCameron Grant { 164285648f9SCameron Grant return 0; 165285648f9SCameron Grant } 166285648f9SCameron Grant 167285648f9SCameron Grant static int 168285648f9SCameron Grant vchan_setformat(kobj_t obj, void *data, u_int32_t format) 169285648f9SCameron Grant { 170285648f9SCameron Grant struct vchinfo *ch = data; 171285648f9SCameron Grant struct pcm_channel *parent = ch->parent; 17212e524a2SDon Lewis struct pcm_channel *channel = ch->channel; 173285648f9SCameron Grant 174285648f9SCameron Grant ch->fmt = format; 17549c5e6e2SCameron Grant ch->bps = 1; 17649c5e6e2SCameron Grant ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; 17787506547SAlexander Leidinger if (ch->fmt & AFMT_16BIT) 17887506547SAlexander Leidinger ch->bps <<= 1; 17987506547SAlexander Leidinger else if (ch->fmt & AFMT_24BIT) 18087506547SAlexander Leidinger ch->bps *= 3; 18187506547SAlexander Leidinger else if (ch->fmt & AFMT_32BIT) 18287506547SAlexander Leidinger ch->bps <<= 2; 18312e524a2SDon Lewis CHN_UNLOCK(channel); 184285648f9SCameron Grant chn_notify(parent, CHN_N_FORMAT); 18512e524a2SDon Lewis CHN_LOCK(channel); 18697d69a96SAlexander Leidinger sndbuf_setfmt(channel->bufsoft, format); 187285648f9SCameron Grant return 0; 188285648f9SCameron Grant } 189285648f9SCameron Grant 190285648f9SCameron Grant static int 191285648f9SCameron Grant vchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 192285648f9SCameron Grant { 193285648f9SCameron Grant struct vchinfo *ch = data; 194285648f9SCameron Grant struct pcm_channel *parent = ch->parent; 19512e524a2SDon Lewis struct pcm_channel *channel = ch->channel; 196285648f9SCameron Grant 197285648f9SCameron Grant ch->spd = speed; 19812e524a2SDon Lewis CHN_UNLOCK(channel); 199285648f9SCameron Grant chn_notify(parent, CHN_N_RATE); 20012e524a2SDon Lewis CHN_LOCK(channel); 201285648f9SCameron Grant return speed; 202285648f9SCameron Grant } 203285648f9SCameron Grant 204285648f9SCameron Grant static int 205285648f9SCameron Grant vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 206285648f9SCameron Grant { 207285648f9SCameron Grant struct vchinfo *ch = data; 20897d69a96SAlexander Leidinger struct pcm_channel *channel = ch->channel; 209285648f9SCameron Grant struct pcm_channel *parent = ch->parent; 21012e524a2SDon Lewis /* struct pcm_channel *channel = ch->channel; */ 21149c5e6e2SCameron Grant int prate, crate; 212285648f9SCameron Grant 213285648f9SCameron Grant ch->blksz = blocksize; 21412e524a2SDon Lewis /* CHN_UNLOCK(channel); */ 21597d69a96SAlexander Leidinger sndbuf_setblksz(channel->bufhard, blocksize); 216285648f9SCameron Grant chn_notify(parent, CHN_N_BLOCKSIZE); 21712e524a2SDon Lewis CHN_LOCK(parent); 21812e524a2SDon Lewis /* CHN_LOCK(channel); */ 21949c5e6e2SCameron Grant 22049c5e6e2SCameron Grant crate = ch->spd * ch->bps; 22149c5e6e2SCameron Grant prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard); 22249c5e6e2SCameron Grant blocksize = sndbuf_getblksz(parent->bufhard); 22312e524a2SDon Lewis CHN_UNLOCK(parent); 22449c5e6e2SCameron Grant blocksize *= prate; 22549c5e6e2SCameron Grant blocksize /= crate; 22649c5e6e2SCameron Grant 227285648f9SCameron Grant return blocksize; 228285648f9SCameron Grant } 229285648f9SCameron Grant 230285648f9SCameron Grant static int 231285648f9SCameron Grant vchan_trigger(kobj_t obj, void *data, int go) 232285648f9SCameron Grant { 233285648f9SCameron Grant struct vchinfo *ch = data; 234285648f9SCameron Grant struct pcm_channel *parent = ch->parent; 23512e524a2SDon Lewis struct pcm_channel *channel = ch->channel; 236285648f9SCameron Grant 237285648f9SCameron Grant if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 238285648f9SCameron Grant return 0; 239285648f9SCameron Grant 240285648f9SCameron Grant ch->run = (go == PCMTRIG_START)? 1 : 0; 24112e524a2SDon Lewis CHN_UNLOCK(channel); 242285648f9SCameron Grant chn_notify(parent, CHN_N_TRIGGER); 24312e524a2SDon Lewis CHN_LOCK(channel); 244285648f9SCameron Grant 245285648f9SCameron Grant return 0; 246285648f9SCameron Grant } 247285648f9SCameron Grant 248285648f9SCameron Grant static struct pcmchan_caps * 249285648f9SCameron Grant vchan_getcaps(kobj_t obj, void *data) 250285648f9SCameron Grant { 251285648f9SCameron Grant struct vchinfo *ch = data; 252285648f9SCameron Grant 253285648f9SCameron Grant ch->caps.minspeed = sndbuf_getspd(ch->parent->bufhard); 254285648f9SCameron Grant ch->caps.maxspeed = ch->caps.minspeed; 255285648f9SCameron Grant ch->caps.fmtlist = vchan_fmt; 256285648f9SCameron Grant ch->caps.caps = 0; 257285648f9SCameron Grant 258285648f9SCameron Grant return &ch->caps; 259285648f9SCameron Grant } 260285648f9SCameron Grant 261285648f9SCameron Grant static kobj_method_t vchan_methods[] = { 262285648f9SCameron Grant KOBJMETHOD(channel_init, vchan_init), 263285648f9SCameron Grant KOBJMETHOD(channel_free, vchan_free), 264285648f9SCameron Grant KOBJMETHOD(channel_setformat, vchan_setformat), 265285648f9SCameron Grant KOBJMETHOD(channel_setspeed, vchan_setspeed), 266285648f9SCameron Grant KOBJMETHOD(channel_setblocksize, vchan_setblocksize), 267285648f9SCameron Grant KOBJMETHOD(channel_trigger, vchan_trigger), 268285648f9SCameron Grant KOBJMETHOD(channel_getcaps, vchan_getcaps), 269285648f9SCameron Grant { 0, 0 } 270285648f9SCameron Grant }; 271285648f9SCameron Grant CHANNEL_DECLARE(vchan); 272285648f9SCameron Grant 27387506547SAlexander Leidinger /* 27487506547SAlexander Leidinger * On the fly vchan rate settings 27587506547SAlexander Leidinger */ 27687506547SAlexander Leidinger #ifdef SND_DYNSYSCTL 27787506547SAlexander Leidinger static int 27887506547SAlexander Leidinger sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) 27987506547SAlexander Leidinger { 28087506547SAlexander Leidinger struct snddev_info *d; 28187506547SAlexander Leidinger struct snddev_channel *sce; 28297d69a96SAlexander Leidinger struct pcm_channel *c, *ch = NULL, *fake; 28387506547SAlexander Leidinger struct pcmchan_caps *caps; 28487506547SAlexander Leidinger int err = 0; 28587506547SAlexander Leidinger int newspd = 0; 28687506547SAlexander Leidinger 28787506547SAlexander Leidinger d = oidp->oid_arg1; 28897d69a96SAlexander Leidinger if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) 28987506547SAlexander Leidinger return EINVAL; 29087506547SAlexander Leidinger SLIST_FOREACH(sce, &d->channels, link) { 29187506547SAlexander Leidinger c = sce->channel; 29287506547SAlexander Leidinger CHN_LOCK(c); 29387506547SAlexander Leidinger if (c->direction == PCMDIR_PLAY) { 29487506547SAlexander Leidinger if (c->flags & CHN_F_VIRTUAL) { 29587506547SAlexander Leidinger if (req->newptr != NULL && 29687506547SAlexander Leidinger (c->flags & CHN_F_BUSY)) { 29787506547SAlexander Leidinger CHN_UNLOCK(c); 29887506547SAlexander Leidinger return EBUSY; 29987506547SAlexander Leidinger } 30097d69a96SAlexander Leidinger if (ch == NULL) 30197d69a96SAlexander Leidinger ch = c->parentchannel; 30287506547SAlexander Leidinger } 30387506547SAlexander Leidinger } 30487506547SAlexander Leidinger CHN_UNLOCK(c); 30587506547SAlexander Leidinger } 30697d69a96SAlexander Leidinger if (ch != NULL) { 30797d69a96SAlexander Leidinger CHN_LOCK(ch); 30897d69a96SAlexander Leidinger newspd = ch->speed; 30997d69a96SAlexander Leidinger CHN_UNLOCK(ch); 31087506547SAlexander Leidinger } 31187506547SAlexander Leidinger err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req); 31287506547SAlexander Leidinger if (err == 0 && req->newptr != NULL) { 31397d69a96SAlexander Leidinger if (ch == NULL || newspd < 1 || 31497d69a96SAlexander Leidinger newspd < feeder_rate_ratemin || 31597d69a96SAlexander Leidinger newspd > feeder_rate_ratemax) 31697d69a96SAlexander Leidinger return EINVAL; 31797d69a96SAlexander Leidinger if (pcm_inprog(d, 1) != 1) { 31897d69a96SAlexander Leidinger pcm_inprog(d, -1); 31997d69a96SAlexander Leidinger return EINPROGRESS; 32097d69a96SAlexander Leidinger } 32197d69a96SAlexander Leidinger CHN_LOCK(ch); 32297d69a96SAlexander Leidinger caps = chn_getcaps(ch); 32397d69a96SAlexander Leidinger if (caps == NULL || newspd < caps->minspeed || 32497d69a96SAlexander Leidinger newspd > caps->maxspeed) { 32597d69a96SAlexander Leidinger CHN_UNLOCK(ch); 32687506547SAlexander Leidinger pcm_inprog(d, -1); 32787506547SAlexander Leidinger return EINVAL; 32887506547SAlexander Leidinger } 32997d69a96SAlexander Leidinger if (newspd != ch->speed) { 33097d69a96SAlexander Leidinger err = chn_setspeed(ch, newspd); 33197d69a96SAlexander Leidinger CHN_UNLOCK(ch); 33297d69a96SAlexander Leidinger if (err == 0) { 33387506547SAlexander Leidinger fake = pcm_getfakechan(d); 33487506547SAlexander Leidinger if (fake != NULL) { 33587506547SAlexander Leidinger CHN_LOCK(fake); 33687506547SAlexander Leidinger fake->speed = newspd; 33787506547SAlexander Leidinger CHN_UNLOCK(fake); 33887506547SAlexander Leidinger } 33987506547SAlexander Leidinger } 34097d69a96SAlexander Leidinger } else 34197d69a96SAlexander Leidinger CHN_UNLOCK(ch); 34287506547SAlexander Leidinger pcm_inprog(d, -1); 34397d69a96SAlexander Leidinger } 34487506547SAlexander Leidinger return err; 34587506547SAlexander Leidinger } 34687506547SAlexander Leidinger #endif 34787506547SAlexander Leidinger 348285648f9SCameron Grant /* virtual channel interface */ 349285648f9SCameron Grant 350285648f9SCameron Grant int 351285648f9SCameron Grant vchan_create(struct pcm_channel *parent) 352285648f9SCameron Grant { 353285648f9SCameron Grant struct snddev_info *d = parent->parentsnddev; 354285648f9SCameron Grant struct pcmchan_children *pce; 35597d69a96SAlexander Leidinger struct pcm_channel *child, *fake; 35697d69a96SAlexander Leidinger struct pcmchan_caps *parent_caps; 35797d69a96SAlexander Leidinger int err, first, speed = 0; 35897d69a96SAlexander Leidinger 35997d69a96SAlexander Leidinger if (!(parent->flags & CHN_F_BUSY)) 36097d69a96SAlexander Leidinger return EBUSY; 36197d69a96SAlexander Leidinger 362285648f9SCameron Grant 36312e524a2SDon Lewis CHN_UNLOCK(parent); 36412e524a2SDon Lewis 365a163d034SWarner Losh pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 36649c5e6e2SCameron Grant if (!pce) { 36712e524a2SDon Lewis CHN_LOCK(parent); 368285648f9SCameron Grant return ENOMEM; 36949c5e6e2SCameron Grant } 370285648f9SCameron Grant 371285648f9SCameron Grant /* create a new playback channel */ 372285648f9SCameron Grant child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 373285648f9SCameron Grant if (!child) { 374285648f9SCameron Grant free(pce, M_DEVBUF); 37512e524a2SDon Lewis CHN_LOCK(parent); 376285648f9SCameron Grant return ENODEV; 377285648f9SCameron Grant } 378285648f9SCameron Grant pce->channel = child; 379285648f9SCameron Grant 380285648f9SCameron Grant /* add us to our grandparent's channel list */ 3815ee30e27SMathew Kanner /* 3825ee30e27SMathew Kanner * XXX maybe we shouldn't always add the dev_t 3835ee30e27SMathew Kanner */ 3845ee30e27SMathew Kanner err = pcm_chn_add(d, child); 385285648f9SCameron Grant if (err) { 386285648f9SCameron Grant pcm_chn_destroy(child); 387285648f9SCameron Grant free(pce, M_DEVBUF); 38897d69a96SAlexander Leidinger CHN_LOCK(parent); 38997d69a96SAlexander Leidinger return err; 390285648f9SCameron Grant } 391285648f9SCameron Grant 39212e524a2SDon Lewis CHN_LOCK(parent); 39397d69a96SAlexander Leidinger /* add us to our parent channel's children */ 39497d69a96SAlexander Leidinger first = SLIST_EMPTY(&parent->children); 39597d69a96SAlexander Leidinger SLIST_INSERT_HEAD(&parent->children, pce, link); 39697d69a96SAlexander Leidinger parent->flags |= CHN_F_HAS_VCHAN; 39787506547SAlexander Leidinger 39897d69a96SAlexander Leidinger if (first) { 39997d69a96SAlexander Leidinger parent_caps = chn_getcaps(parent); 40097d69a96SAlexander Leidinger if (parent_caps == NULL) 40197d69a96SAlexander Leidinger err = EINVAL; 40297d69a96SAlexander Leidinger 40397d69a96SAlexander Leidinger if (!err) 404285648f9SCameron Grant err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); 40587506547SAlexander Leidinger 40697d69a96SAlexander Leidinger if (!err) { 40787506547SAlexander Leidinger fake = pcm_getfakechan(d); 40887506547SAlexander Leidinger if (fake != NULL) { 40987506547SAlexander Leidinger /* 41097d69a96SAlexander Leidinger * Avoid querying kernel hint, use saved value 41197d69a96SAlexander Leidinger * from fake channel. 41287506547SAlexander Leidinger */ 41397d69a96SAlexander Leidinger CHN_UNLOCK(parent); 41487506547SAlexander Leidinger CHN_LOCK(fake); 41587506547SAlexander Leidinger speed = fake->speed; 41687506547SAlexander Leidinger CHN_UNLOCK(fake); 41797d69a96SAlexander Leidinger CHN_LOCK(parent); 41887506547SAlexander Leidinger } 41997d69a96SAlexander Leidinger 42087506547SAlexander Leidinger /* 42187506547SAlexander Leidinger * This is very sad. Few soundcards advertised as being 42287506547SAlexander Leidinger * able to do (insanely) higher/lower speed, but in 42387506547SAlexander Leidinger * reality, they simply can't. At least, we give user chance 42497d69a96SAlexander Leidinger * to set sane value via kernel hints or sysctl. 42587506547SAlexander Leidinger */ 42697d69a96SAlexander Leidinger if (speed < 1) { 42797d69a96SAlexander Leidinger int r; 42897d69a96SAlexander Leidinger CHN_UNLOCK(parent); 42997d69a96SAlexander Leidinger r = resource_int_value(device_get_name(parent->dev), 43087506547SAlexander Leidinger device_get_unit(parent->dev), 43197d69a96SAlexander Leidinger "vchanrate", &speed); 43297d69a96SAlexander Leidinger CHN_LOCK(parent); 43397d69a96SAlexander Leidinger if (r != 0) 43487506547SAlexander Leidinger speed = VCHAN_DEFAULT_SPEED; 43587506547SAlexander Leidinger } 43687506547SAlexander Leidinger 43787506547SAlexander Leidinger /* 43887506547SAlexander Leidinger * Limit speed based on driver caps. 43987506547SAlexander Leidinger * This is supposed to help fixed rate, non-VRA 44087506547SAlexander Leidinger * AC97 cards, but.. (see below) 44187506547SAlexander Leidinger */ 44287506547SAlexander Leidinger if (speed < parent_caps->minspeed) 44387506547SAlexander Leidinger speed = parent_caps->minspeed; 44487506547SAlexander Leidinger if (speed > parent_caps->maxspeed) 44587506547SAlexander Leidinger speed = parent_caps->maxspeed; 44687506547SAlexander Leidinger 44787506547SAlexander Leidinger /* 44887506547SAlexander Leidinger * We still need to limit the speed between 44987506547SAlexander Leidinger * feeder_rate_ratemin <-> feeder_rate_ratemax. This is 45087506547SAlexander Leidinger * just an escape goat if all of the above failed 45187506547SAlexander Leidinger * miserably. 45287506547SAlexander Leidinger */ 45387506547SAlexander Leidinger if (speed < feeder_rate_ratemin) 45487506547SAlexander Leidinger speed = feeder_rate_ratemin; 45587506547SAlexander Leidinger if (speed > feeder_rate_ratemax) 45687506547SAlexander Leidinger speed = feeder_rate_ratemax; 45787506547SAlexander Leidinger 45887506547SAlexander Leidinger err = chn_setspeed(parent, speed); 45997d69a96SAlexander Leidinger 46097d69a96SAlexander Leidinger if (!err && fake != NULL) { 46187506547SAlexander Leidinger /* 46287506547SAlexander Leidinger * Save new value to fake channel. 46387506547SAlexander Leidinger */ 46497d69a96SAlexander Leidinger CHN_UNLOCK(parent); 46587506547SAlexander Leidinger CHN_LOCK(fake); 46687506547SAlexander Leidinger fake->speed = speed; 46787506547SAlexander Leidinger CHN_UNLOCK(fake); 46897d69a96SAlexander Leidinger CHN_LOCK(parent); 46987506547SAlexander Leidinger } 470285648f9SCameron Grant } 471285648f9SCameron Grant 47297d69a96SAlexander Leidinger if (err) { 47397d69a96SAlexander Leidinger SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 47497d69a96SAlexander Leidinger parent->flags &= ~CHN_F_HAS_VCHAN; 47597d69a96SAlexander Leidinger CHN_UNLOCK(parent); 47697d69a96SAlexander Leidinger free(pce, M_DEVBUF); 47797d69a96SAlexander Leidinger pcm_chn_remove(d, child); 47897d69a96SAlexander Leidinger pcm_chn_destroy(child); 47997d69a96SAlexander Leidinger CHN_LOCK(parent); 480285648f9SCameron Grant return err; 481285648f9SCameron Grant } 48297d69a96SAlexander Leidinger } 48397d69a96SAlexander Leidinger 48497d69a96SAlexander Leidinger return 0; 48597d69a96SAlexander Leidinger } 486285648f9SCameron Grant 487285648f9SCameron Grant int 488285648f9SCameron Grant vchan_destroy(struct pcm_channel *c) 489285648f9SCameron Grant { 490285648f9SCameron Grant struct pcm_channel *parent = c->parentchannel; 491285648f9SCameron Grant struct snddev_info *d = parent->parentsnddev; 492285648f9SCameron Grant struct pcmchan_children *pce; 49397d69a96SAlexander Leidinger struct snddev_channel *sce; 494f637a36cSCameron Grant int err, last; 495285648f9SCameron Grant 49649c5e6e2SCameron Grant CHN_LOCK(parent); 49749c5e6e2SCameron Grant if (!(parent->flags & CHN_F_BUSY)) { 49849c5e6e2SCameron Grant CHN_UNLOCK(parent); 49949c5e6e2SCameron Grant return EBUSY; 50049c5e6e2SCameron Grant } 50149c5e6e2SCameron Grant if (SLIST_EMPTY(&parent->children)) { 50249c5e6e2SCameron Grant CHN_UNLOCK(parent); 50349c5e6e2SCameron Grant return EINVAL; 50449c5e6e2SCameron Grant } 50549c5e6e2SCameron Grant 506285648f9SCameron Grant /* remove us from our parent's children list */ 507285648f9SCameron Grant SLIST_FOREACH(pce, &parent->children, link) { 508285648f9SCameron Grant if (pce->channel == c) 509285648f9SCameron Grant goto gotch; 510285648f9SCameron Grant } 51149c5e6e2SCameron Grant CHN_UNLOCK(parent); 512285648f9SCameron Grant return EINVAL; 513285648f9SCameron Grant gotch: 51497d69a96SAlexander Leidinger SLIST_FOREACH(sce, &d->channels, link) { 51597d69a96SAlexander Leidinger if (sce->channel == c) { 51697d69a96SAlexander Leidinger if (sce->dsp_devt) 51797d69a96SAlexander Leidinger destroy_dev(sce->dsp_devt); 51897d69a96SAlexander Leidinger if (sce->dspW_devt) 51997d69a96SAlexander Leidinger destroy_dev(sce->dspW_devt); 52097d69a96SAlexander Leidinger if (sce->audio_devt) 52197d69a96SAlexander Leidinger destroy_dev(sce->audio_devt); 52297d69a96SAlexander Leidinger if (sce->dspr_devt) 52397d69a96SAlexander Leidinger destroy_dev(sce->dspr_devt); 52497d69a96SAlexander Leidinger break; 52597d69a96SAlexander Leidinger } 52697d69a96SAlexander Leidinger } 527285648f9SCameron Grant SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 528285648f9SCameron Grant free(pce, M_DEVBUF); 529285648f9SCameron Grant 530f637a36cSCameron Grant last = SLIST_EMPTY(&parent->children); 53197d69a96SAlexander Leidinger if (last) { 532285648f9SCameron Grant parent->flags &= ~CHN_F_BUSY; 53397d69a96SAlexander Leidinger parent->flags &= ~CHN_F_HAS_VCHAN; 53497d69a96SAlexander Leidinger } 535f637a36cSCameron Grant 53645550658SPoul-Henning Kamp /* remove us from our grandparent's channel list */ 5375ee30e27SMathew Kanner err = pcm_chn_remove(d, c); 538f637a36cSCameron Grant 53949c5e6e2SCameron Grant CHN_UNLOCK(parent); 540285648f9SCameron Grant /* destroy ourselves */ 54197d69a96SAlexander Leidinger if (!err) 542285648f9SCameron Grant err = pcm_chn_destroy(c); 543285648f9SCameron Grant 54497d69a96SAlexander Leidinger #if 0 54597d69a96SAlexander Leidinger if (!err && last) { 54697d69a96SAlexander Leidinger CHN_LOCK(parent); 54797d69a96SAlexander Leidinger chn_reset(parent, chn_getcaps(parent)->fmtlist[0]); 54897d69a96SAlexander Leidinger chn_setspeed(parent, chn_getcaps(parent)->minspeed); 54997d69a96SAlexander Leidinger CHN_UNLOCK(parent); 55097d69a96SAlexander Leidinger } 55197d69a96SAlexander Leidinger #endif 55297d69a96SAlexander Leidinger 553285648f9SCameron Grant return err; 554285648f9SCameron Grant } 555285648f9SCameron Grant 556285648f9SCameron Grant int 55767b1dce3SCameron Grant vchan_initsys(device_t dev) 558285648f9SCameron Grant { 559285648f9SCameron Grant #ifdef SND_DYNSYSCTL 56067b1dce3SCameron Grant struct snddev_info *d; 56167b1dce3SCameron Grant 56267b1dce3SCameron Grant d = device_get_softc(dev); 56367b1dce3SCameron Grant SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 564285648f9SCameron Grant OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 56564d85ef7SMark Murray sysctl_hw_snd_vchans, "I", ""); 56687506547SAlexander Leidinger SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 56787506547SAlexander Leidinger OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 56887506547SAlexander Leidinger sysctl_hw_snd_vchanrate, "I", ""); 569285648f9SCameron Grant #endif 57067b1dce3SCameron Grant 571285648f9SCameron Grant return 0; 572285648f9SCameron Grant } 573