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); 199d45d1f20SAriff Abdullah CHN_LOCK(parent); 200d45d1f20SAriff Abdullah speed = sndbuf_getspd(parent->bufsoft); 201d45d1f20SAriff Abdullah CHN_UNLOCK(parent); 20212e524a2SDon Lewis CHN_LOCK(channel); 203285648f9SCameron Grant return speed; 204285648f9SCameron Grant } 205285648f9SCameron Grant 206285648f9SCameron Grant static int 207285648f9SCameron Grant vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 208285648f9SCameron Grant { 209285648f9SCameron Grant struct vchinfo *ch = data; 21097d69a96SAlexander Leidinger struct pcm_channel *channel = ch->channel; 211285648f9SCameron Grant struct pcm_channel *parent = ch->parent; 21212e524a2SDon Lewis /* struct pcm_channel *channel = ch->channel; */ 21349c5e6e2SCameron Grant int prate, crate; 214285648f9SCameron Grant 215285648f9SCameron Grant ch->blksz = blocksize; 21612e524a2SDon Lewis /* CHN_UNLOCK(channel); */ 21797d69a96SAlexander Leidinger sndbuf_setblksz(channel->bufhard, blocksize); 218285648f9SCameron Grant chn_notify(parent, CHN_N_BLOCKSIZE); 21912e524a2SDon Lewis CHN_LOCK(parent); 22012e524a2SDon Lewis /* CHN_LOCK(channel); */ 22149c5e6e2SCameron Grant 22249c5e6e2SCameron Grant crate = ch->spd * ch->bps; 223d45d1f20SAriff Abdullah prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft); 224d45d1f20SAriff Abdullah blocksize = sndbuf_getblksz(parent->bufsoft); 22512e524a2SDon Lewis CHN_UNLOCK(parent); 22649c5e6e2SCameron Grant blocksize *= prate; 22749c5e6e2SCameron Grant blocksize /= crate; 22849c5e6e2SCameron Grant 229285648f9SCameron Grant return blocksize; 230285648f9SCameron Grant } 231285648f9SCameron Grant 232285648f9SCameron Grant static int 233285648f9SCameron Grant vchan_trigger(kobj_t obj, void *data, int go) 234285648f9SCameron Grant { 235285648f9SCameron Grant struct vchinfo *ch = data; 236285648f9SCameron Grant struct pcm_channel *parent = ch->parent; 23712e524a2SDon Lewis struct pcm_channel *channel = ch->channel; 238285648f9SCameron Grant 239285648f9SCameron Grant if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 240285648f9SCameron Grant return 0; 241285648f9SCameron Grant 242285648f9SCameron Grant ch->run = (go == PCMTRIG_START)? 1 : 0; 24312e524a2SDon Lewis CHN_UNLOCK(channel); 244285648f9SCameron Grant chn_notify(parent, CHN_N_TRIGGER); 24512e524a2SDon Lewis CHN_LOCK(channel); 246285648f9SCameron Grant 247285648f9SCameron Grant return 0; 248285648f9SCameron Grant } 249285648f9SCameron Grant 250285648f9SCameron Grant static struct pcmchan_caps * 251285648f9SCameron Grant vchan_getcaps(kobj_t obj, void *data) 252285648f9SCameron Grant { 253285648f9SCameron Grant struct vchinfo *ch = data; 254285648f9SCameron Grant 255d45d1f20SAriff Abdullah ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft); 256285648f9SCameron Grant ch->caps.maxspeed = ch->caps.minspeed; 257285648f9SCameron Grant ch->caps.fmtlist = vchan_fmt; 258285648f9SCameron Grant ch->caps.caps = 0; 259285648f9SCameron Grant 260285648f9SCameron Grant return &ch->caps; 261285648f9SCameron Grant } 262285648f9SCameron Grant 263285648f9SCameron Grant static kobj_method_t vchan_methods[] = { 264285648f9SCameron Grant KOBJMETHOD(channel_init, vchan_init), 265285648f9SCameron Grant KOBJMETHOD(channel_free, vchan_free), 266285648f9SCameron Grant KOBJMETHOD(channel_setformat, vchan_setformat), 267285648f9SCameron Grant KOBJMETHOD(channel_setspeed, vchan_setspeed), 268285648f9SCameron Grant KOBJMETHOD(channel_setblocksize, vchan_setblocksize), 269285648f9SCameron Grant KOBJMETHOD(channel_trigger, vchan_trigger), 270285648f9SCameron Grant KOBJMETHOD(channel_getcaps, vchan_getcaps), 271285648f9SCameron Grant { 0, 0 } 272285648f9SCameron Grant }; 273285648f9SCameron Grant CHANNEL_DECLARE(vchan); 274285648f9SCameron Grant 27587506547SAlexander Leidinger /* 27687506547SAlexander Leidinger * On the fly vchan rate settings 27787506547SAlexander Leidinger */ 27887506547SAlexander Leidinger #ifdef SND_DYNSYSCTL 27987506547SAlexander Leidinger static int 28087506547SAlexander Leidinger sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) 28187506547SAlexander Leidinger { 28287506547SAlexander Leidinger struct snddev_info *d; 28387506547SAlexander Leidinger struct snddev_channel *sce; 28497d69a96SAlexander Leidinger struct pcm_channel *c, *ch = NULL, *fake; 28587506547SAlexander Leidinger struct pcmchan_caps *caps; 28687506547SAlexander Leidinger int err = 0; 28787506547SAlexander Leidinger int newspd = 0; 28887506547SAlexander Leidinger 28987506547SAlexander Leidinger d = oidp->oid_arg1; 29097d69a96SAlexander Leidinger if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) 29187506547SAlexander Leidinger return EINVAL; 29287506547SAlexander Leidinger SLIST_FOREACH(sce, &d->channels, link) { 29387506547SAlexander Leidinger c = sce->channel; 29487506547SAlexander Leidinger CHN_LOCK(c); 29587506547SAlexander Leidinger if (c->direction == PCMDIR_PLAY) { 29687506547SAlexander Leidinger if (c->flags & CHN_F_VIRTUAL) { 29787506547SAlexander Leidinger if (req->newptr != NULL && 29887506547SAlexander Leidinger (c->flags & CHN_F_BUSY)) { 29987506547SAlexander Leidinger CHN_UNLOCK(c); 30087506547SAlexander Leidinger return EBUSY; 30187506547SAlexander Leidinger } 30297d69a96SAlexander Leidinger if (ch == NULL) 30397d69a96SAlexander Leidinger ch = c->parentchannel; 30487506547SAlexander Leidinger } 30587506547SAlexander Leidinger } 30687506547SAlexander Leidinger CHN_UNLOCK(c); 30787506547SAlexander Leidinger } 30897d69a96SAlexander Leidinger if (ch != NULL) { 30997d69a96SAlexander Leidinger CHN_LOCK(ch); 31097d69a96SAlexander Leidinger newspd = ch->speed; 31197d69a96SAlexander Leidinger CHN_UNLOCK(ch); 31287506547SAlexander Leidinger } 31387506547SAlexander Leidinger err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req); 31487506547SAlexander Leidinger if (err == 0 && req->newptr != NULL) { 31597d69a96SAlexander Leidinger if (ch == NULL || newspd < 1 || 31697d69a96SAlexander Leidinger newspd < feeder_rate_ratemin || 31797d69a96SAlexander Leidinger newspd > feeder_rate_ratemax) 31897d69a96SAlexander Leidinger return EINVAL; 31997d69a96SAlexander Leidinger if (pcm_inprog(d, 1) != 1) { 32097d69a96SAlexander Leidinger pcm_inprog(d, -1); 32197d69a96SAlexander Leidinger return EINPROGRESS; 32297d69a96SAlexander Leidinger } 32397d69a96SAlexander Leidinger CHN_LOCK(ch); 32497d69a96SAlexander Leidinger caps = chn_getcaps(ch); 32597d69a96SAlexander Leidinger if (caps == NULL || newspd < caps->minspeed || 32697d69a96SAlexander Leidinger newspd > caps->maxspeed) { 32797d69a96SAlexander Leidinger CHN_UNLOCK(ch); 32887506547SAlexander Leidinger pcm_inprog(d, -1); 32987506547SAlexander Leidinger return EINVAL; 33087506547SAlexander Leidinger } 33197d69a96SAlexander Leidinger if (newspd != ch->speed) { 33297d69a96SAlexander Leidinger err = chn_setspeed(ch, newspd); 333d45d1f20SAriff Abdullah /* 334d45d1f20SAriff Abdullah * Try to avoid FEEDER_RATE on parent channel if the 335d45d1f20SAriff Abdullah * requested value is not supported by the hardware. 336d45d1f20SAriff Abdullah */ 337d45d1f20SAriff Abdullah if (!err && (ch->feederflags & (1 << FEEDER_RATE))) { 338d45d1f20SAriff Abdullah newspd = sndbuf_getspd(ch->bufhard); 339d45d1f20SAriff Abdullah err = chn_setspeed(ch, newspd); 340d45d1f20SAriff Abdullah } 34197d69a96SAlexander Leidinger CHN_UNLOCK(ch); 34297d69a96SAlexander Leidinger if (err == 0) { 34387506547SAlexander Leidinger fake = pcm_getfakechan(d); 34487506547SAlexander Leidinger if (fake != NULL) { 34587506547SAlexander Leidinger CHN_LOCK(fake); 34687506547SAlexander Leidinger fake->speed = newspd; 34787506547SAlexander Leidinger CHN_UNLOCK(fake); 34887506547SAlexander Leidinger } 34987506547SAlexander Leidinger } 35097d69a96SAlexander Leidinger } else 35197d69a96SAlexander Leidinger CHN_UNLOCK(ch); 35287506547SAlexander Leidinger pcm_inprog(d, -1); 35397d69a96SAlexander Leidinger } 35487506547SAlexander Leidinger return err; 35587506547SAlexander Leidinger } 35687506547SAlexander Leidinger #endif 35787506547SAlexander Leidinger 358285648f9SCameron Grant /* virtual channel interface */ 359285648f9SCameron Grant 360285648f9SCameron Grant int 361285648f9SCameron Grant vchan_create(struct pcm_channel *parent) 362285648f9SCameron Grant { 363285648f9SCameron Grant struct snddev_info *d = parent->parentsnddev; 364285648f9SCameron Grant struct pcmchan_children *pce; 36597d69a96SAlexander Leidinger struct pcm_channel *child, *fake; 36697d69a96SAlexander Leidinger struct pcmchan_caps *parent_caps; 36797d69a96SAlexander Leidinger int err, first, speed = 0; 36897d69a96SAlexander Leidinger 36997d69a96SAlexander Leidinger if (!(parent->flags & CHN_F_BUSY)) 37097d69a96SAlexander Leidinger return EBUSY; 37197d69a96SAlexander Leidinger 372285648f9SCameron Grant 37312e524a2SDon Lewis CHN_UNLOCK(parent); 37412e524a2SDon Lewis 375a163d034SWarner Losh pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 37649c5e6e2SCameron Grant if (!pce) { 37712e524a2SDon Lewis CHN_LOCK(parent); 378285648f9SCameron Grant return ENOMEM; 37949c5e6e2SCameron Grant } 380285648f9SCameron Grant 381285648f9SCameron Grant /* create a new playback channel */ 382285648f9SCameron Grant child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 383285648f9SCameron Grant if (!child) { 384285648f9SCameron Grant free(pce, M_DEVBUF); 38512e524a2SDon Lewis CHN_LOCK(parent); 386285648f9SCameron Grant return ENODEV; 387285648f9SCameron Grant } 388285648f9SCameron Grant pce->channel = child; 389285648f9SCameron Grant 390285648f9SCameron Grant /* add us to our grandparent's channel list */ 3915ee30e27SMathew Kanner /* 3925ee30e27SMathew Kanner * XXX maybe we shouldn't always add the dev_t 3935ee30e27SMathew Kanner */ 3945ee30e27SMathew Kanner err = pcm_chn_add(d, child); 395285648f9SCameron Grant if (err) { 396285648f9SCameron Grant pcm_chn_destroy(child); 397285648f9SCameron Grant free(pce, M_DEVBUF); 39897d69a96SAlexander Leidinger CHN_LOCK(parent); 39997d69a96SAlexander Leidinger return err; 400285648f9SCameron Grant } 401285648f9SCameron Grant 40212e524a2SDon Lewis CHN_LOCK(parent); 40397d69a96SAlexander Leidinger /* add us to our parent channel's children */ 40497d69a96SAlexander Leidinger first = SLIST_EMPTY(&parent->children); 40597d69a96SAlexander Leidinger SLIST_INSERT_HEAD(&parent->children, pce, link); 40697d69a96SAlexander Leidinger parent->flags |= CHN_F_HAS_VCHAN; 40787506547SAlexander Leidinger 40897d69a96SAlexander Leidinger if (first) { 40997d69a96SAlexander Leidinger parent_caps = chn_getcaps(parent); 41097d69a96SAlexander Leidinger if (parent_caps == NULL) 41197d69a96SAlexander Leidinger err = EINVAL; 41297d69a96SAlexander Leidinger 41397d69a96SAlexander Leidinger if (!err) 414285648f9SCameron Grant err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); 41587506547SAlexander Leidinger 41697d69a96SAlexander Leidinger if (!err) { 41787506547SAlexander Leidinger fake = pcm_getfakechan(d); 41887506547SAlexander Leidinger if (fake != NULL) { 41987506547SAlexander Leidinger /* 42097d69a96SAlexander Leidinger * Avoid querying kernel hint, use saved value 42197d69a96SAlexander Leidinger * from fake channel. 42287506547SAlexander Leidinger */ 42397d69a96SAlexander Leidinger CHN_UNLOCK(parent); 42487506547SAlexander Leidinger CHN_LOCK(fake); 42587506547SAlexander Leidinger speed = fake->speed; 42687506547SAlexander Leidinger CHN_UNLOCK(fake); 42797d69a96SAlexander Leidinger CHN_LOCK(parent); 42887506547SAlexander Leidinger } 42997d69a96SAlexander Leidinger 43087506547SAlexander Leidinger /* 43187506547SAlexander Leidinger * This is very sad. Few soundcards advertised as being 43287506547SAlexander Leidinger * able to do (insanely) higher/lower speed, but in 43387506547SAlexander Leidinger * reality, they simply can't. At least, we give user chance 43497d69a96SAlexander Leidinger * to set sane value via kernel hints or sysctl. 43587506547SAlexander Leidinger */ 43697d69a96SAlexander Leidinger if (speed < 1) { 43797d69a96SAlexander Leidinger int r; 43897d69a96SAlexander Leidinger CHN_UNLOCK(parent); 43997d69a96SAlexander Leidinger r = resource_int_value(device_get_name(parent->dev), 44087506547SAlexander Leidinger device_get_unit(parent->dev), 44197d69a96SAlexander Leidinger "vchanrate", &speed); 44297d69a96SAlexander Leidinger CHN_LOCK(parent); 44397d69a96SAlexander Leidinger if (r != 0) 44487506547SAlexander Leidinger speed = VCHAN_DEFAULT_SPEED; 44587506547SAlexander Leidinger } 44687506547SAlexander Leidinger 44787506547SAlexander Leidinger /* 44887506547SAlexander Leidinger * Limit speed based on driver caps. 44987506547SAlexander Leidinger * This is supposed to help fixed rate, non-VRA 45087506547SAlexander Leidinger * AC97 cards, but.. (see below) 45187506547SAlexander Leidinger */ 45287506547SAlexander Leidinger if (speed < parent_caps->minspeed) 45387506547SAlexander Leidinger speed = parent_caps->minspeed; 45487506547SAlexander Leidinger if (speed > parent_caps->maxspeed) 45587506547SAlexander Leidinger speed = parent_caps->maxspeed; 45687506547SAlexander Leidinger 45787506547SAlexander Leidinger /* 45887506547SAlexander Leidinger * We still need to limit the speed between 45987506547SAlexander Leidinger * feeder_rate_ratemin <-> feeder_rate_ratemax. This is 46087506547SAlexander Leidinger * just an escape goat if all of the above failed 46187506547SAlexander Leidinger * miserably. 46287506547SAlexander Leidinger */ 46387506547SAlexander Leidinger if (speed < feeder_rate_ratemin) 46487506547SAlexander Leidinger speed = feeder_rate_ratemin; 46587506547SAlexander Leidinger if (speed > feeder_rate_ratemax) 46687506547SAlexander Leidinger speed = feeder_rate_ratemax; 46787506547SAlexander Leidinger 46887506547SAlexander Leidinger err = chn_setspeed(parent, speed); 469d45d1f20SAriff Abdullah /* 470d45d1f20SAriff Abdullah * Try to avoid FEEDER_RATE on parent channel if the 471d45d1f20SAriff Abdullah * requested value is not supported by the hardware. 472d45d1f20SAriff Abdullah */ 473d45d1f20SAriff Abdullah if (!err && (parent->feederflags & (1 << FEEDER_RATE))) { 474d45d1f20SAriff Abdullah speed = sndbuf_getspd(parent->bufhard); 475d45d1f20SAriff Abdullah err = chn_setspeed(parent, speed); 476d45d1f20SAriff Abdullah } 47797d69a96SAlexander Leidinger 47897d69a96SAlexander Leidinger if (!err && fake != NULL) { 47987506547SAlexander Leidinger /* 48087506547SAlexander Leidinger * Save new value to fake channel. 48187506547SAlexander Leidinger */ 48297d69a96SAlexander Leidinger CHN_UNLOCK(parent); 48387506547SAlexander Leidinger CHN_LOCK(fake); 48487506547SAlexander Leidinger fake->speed = speed; 48587506547SAlexander Leidinger CHN_UNLOCK(fake); 48697d69a96SAlexander Leidinger CHN_LOCK(parent); 48787506547SAlexander Leidinger } 488285648f9SCameron Grant } 489285648f9SCameron Grant 49097d69a96SAlexander Leidinger if (err) { 49197d69a96SAlexander Leidinger SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 49297d69a96SAlexander Leidinger parent->flags &= ~CHN_F_HAS_VCHAN; 49397d69a96SAlexander Leidinger CHN_UNLOCK(parent); 49497d69a96SAlexander Leidinger free(pce, M_DEVBUF); 49597d69a96SAlexander Leidinger pcm_chn_remove(d, child); 49697d69a96SAlexander Leidinger pcm_chn_destroy(child); 49797d69a96SAlexander Leidinger CHN_LOCK(parent); 498285648f9SCameron Grant return err; 499285648f9SCameron Grant } 50097d69a96SAlexander Leidinger } 50197d69a96SAlexander Leidinger 50297d69a96SAlexander Leidinger return 0; 50397d69a96SAlexander Leidinger } 504285648f9SCameron Grant 505285648f9SCameron Grant int 506285648f9SCameron Grant vchan_destroy(struct pcm_channel *c) 507285648f9SCameron Grant { 508285648f9SCameron Grant struct pcm_channel *parent = c->parentchannel; 509285648f9SCameron Grant struct snddev_info *d = parent->parentsnddev; 510285648f9SCameron Grant struct pcmchan_children *pce; 51197d69a96SAlexander Leidinger struct snddev_channel *sce; 512f637a36cSCameron Grant int err, last; 513285648f9SCameron Grant 51449c5e6e2SCameron Grant CHN_LOCK(parent); 51549c5e6e2SCameron Grant if (!(parent->flags & CHN_F_BUSY)) { 51649c5e6e2SCameron Grant CHN_UNLOCK(parent); 51749c5e6e2SCameron Grant return EBUSY; 51849c5e6e2SCameron Grant } 51949c5e6e2SCameron Grant if (SLIST_EMPTY(&parent->children)) { 52049c5e6e2SCameron Grant CHN_UNLOCK(parent); 52149c5e6e2SCameron Grant return EINVAL; 52249c5e6e2SCameron Grant } 52349c5e6e2SCameron Grant 524285648f9SCameron Grant /* remove us from our parent's children list */ 525285648f9SCameron Grant SLIST_FOREACH(pce, &parent->children, link) { 526285648f9SCameron Grant if (pce->channel == c) 527285648f9SCameron Grant goto gotch; 528285648f9SCameron Grant } 52949c5e6e2SCameron Grant CHN_UNLOCK(parent); 530285648f9SCameron Grant return EINVAL; 531285648f9SCameron Grant gotch: 53297d69a96SAlexander Leidinger SLIST_FOREACH(sce, &d->channels, link) { 53397d69a96SAlexander Leidinger if (sce->channel == c) { 53497d69a96SAlexander Leidinger if (sce->dsp_devt) 53597d69a96SAlexander Leidinger destroy_dev(sce->dsp_devt); 53697d69a96SAlexander Leidinger if (sce->dspW_devt) 53797d69a96SAlexander Leidinger destroy_dev(sce->dspW_devt); 53897d69a96SAlexander Leidinger if (sce->audio_devt) 53997d69a96SAlexander Leidinger destroy_dev(sce->audio_devt); 54097d69a96SAlexander Leidinger if (sce->dspr_devt) 54197d69a96SAlexander Leidinger destroy_dev(sce->dspr_devt); 54297d69a96SAlexander Leidinger break; 54397d69a96SAlexander Leidinger } 54497d69a96SAlexander Leidinger } 545285648f9SCameron Grant SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 546285648f9SCameron Grant free(pce, M_DEVBUF); 547285648f9SCameron Grant 548f637a36cSCameron Grant last = SLIST_EMPTY(&parent->children); 54997d69a96SAlexander Leidinger if (last) { 550285648f9SCameron Grant parent->flags &= ~CHN_F_BUSY; 55197d69a96SAlexander Leidinger parent->flags &= ~CHN_F_HAS_VCHAN; 55297d69a96SAlexander Leidinger } 553f637a36cSCameron Grant 55445550658SPoul-Henning Kamp /* remove us from our grandparent's channel list */ 5555ee30e27SMathew Kanner err = pcm_chn_remove(d, c); 556f637a36cSCameron Grant 55749c5e6e2SCameron Grant CHN_UNLOCK(parent); 558285648f9SCameron Grant /* destroy ourselves */ 55997d69a96SAlexander Leidinger if (!err) 560285648f9SCameron Grant err = pcm_chn_destroy(c); 561285648f9SCameron Grant 56297d69a96SAlexander Leidinger #if 0 56397d69a96SAlexander Leidinger if (!err && last) { 56497d69a96SAlexander Leidinger CHN_LOCK(parent); 56597d69a96SAlexander Leidinger chn_reset(parent, chn_getcaps(parent)->fmtlist[0]); 56697d69a96SAlexander Leidinger chn_setspeed(parent, chn_getcaps(parent)->minspeed); 56797d69a96SAlexander Leidinger CHN_UNLOCK(parent); 56897d69a96SAlexander Leidinger } 56997d69a96SAlexander Leidinger #endif 57097d69a96SAlexander Leidinger 571285648f9SCameron Grant return err; 572285648f9SCameron Grant } 573285648f9SCameron Grant 574285648f9SCameron Grant int 57567b1dce3SCameron Grant vchan_initsys(device_t dev) 576285648f9SCameron Grant { 577285648f9SCameron Grant #ifdef SND_DYNSYSCTL 57867b1dce3SCameron Grant struct snddev_info *d; 57967b1dce3SCameron Grant 58067b1dce3SCameron Grant d = device_get_softc(dev); 58167b1dce3SCameron Grant SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 582285648f9SCameron Grant OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 58364d85ef7SMark Murray sysctl_hw_snd_vchans, "I", ""); 58487506547SAlexander Leidinger SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 58587506547SAlexander Leidinger OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 58687506547SAlexander Leidinger sysctl_hw_snd_vchanrate, "I", ""); 587285648f9SCameron Grant #endif 58867b1dce3SCameron Grant 589285648f9SCameron Grant return 0; 590285648f9SCameron Grant } 591