1285648f9SCameron Grant /* 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 33285648f9SCameron Grant struct vchinfo { 3449c5e6e2SCameron Grant u_int32_t spd, fmt, blksz, bps, run; 35285648f9SCameron Grant struct pcm_channel *channel, *parent; 36285648f9SCameron Grant struct pcmchan_caps caps; 37285648f9SCameron Grant }; 38285648f9SCameron Grant 39285648f9SCameron Grant static u_int32_t vchan_fmt[] = { 40285648f9SCameron Grant AFMT_STEREO | AFMT_S16_LE, 41285648f9SCameron Grant 0 42285648f9SCameron Grant }; 43285648f9SCameron Grant 44285648f9SCameron Grant static int 45285648f9SCameron Grant vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count) 46285648f9SCameron Grant { 47285648f9SCameron Grant /* 48285648f9SCameron Grant * to is the output buffer, tmp is the input buffer 49285648f9SCameron Grant * count is the number of 16bit samples to mix 50285648f9SCameron Grant */ 51285648f9SCameron Grant int i; 52285648f9SCameron Grant int x; 53285648f9SCameron Grant 54285648f9SCameron Grant for(i = 0; i < count; i++) { 55285648f9SCameron Grant x = to[i]; 56285648f9SCameron Grant x += tmp[i]; 57285648f9SCameron Grant if (x < -32768) { 58285648f9SCameron Grant /* printf("%d + %d = %d (u)\n", to[i], tmp[i], x); */ 59285648f9SCameron Grant x = -32768; 60285648f9SCameron Grant } 61285648f9SCameron Grant if (x > 32767) { 62285648f9SCameron Grant /* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */ 63285648f9SCameron Grant x = 32767; 64285648f9SCameron Grant } 65285648f9SCameron Grant to[i] = x & 0x0000ffff; 66285648f9SCameron Grant } 67285648f9SCameron Grant return 0; 68285648f9SCameron Grant } 69285648f9SCameron Grant 70285648f9SCameron Grant static int 71285648f9SCameron Grant feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) 72285648f9SCameron Grant { 73285648f9SCameron Grant /* we're going to abuse things a bit */ 74285648f9SCameron Grant struct snd_dbuf *src = source; 75285648f9SCameron Grant struct pcmchan_children *cce; 76285648f9SCameron Grant struct pcm_channel *ch; 77285648f9SCameron Grant int16_t *tmp, *dst; 78285648f9SCameron Grant unsigned int cnt; 79285648f9SCameron Grant 80285648f9SCameron Grant KASSERT(sndbuf_getsize(src) >= count, ("bad bufsize")); 81285648f9SCameron Grant count &= ~1; 82285648f9SCameron Grant bzero(b, count); 83285648f9SCameron Grant 84285648f9SCameron Grant /* 85285648f9SCameron Grant * we are going to use our source as a temporary buffer since it's 86285648f9SCameron Grant * got no other purpose. we obtain our data by traversing the channel 87285648f9SCameron Grant * list of children and calling vchan_mix_* to mix count bytes from each 88285648f9SCameron Grant * into our destination buffer, b 89285648f9SCameron Grant */ 90285648f9SCameron Grant dst = (int16_t *)b; 91285648f9SCameron Grant tmp = (int16_t *)sndbuf_getbuf(src); 92285648f9SCameron Grant bzero(tmp, count); 93285648f9SCameron Grant SLIST_FOREACH(cce, &c->children, link) { 94285648f9SCameron Grant ch = cce->channel; 9549c5e6e2SCameron Grant if (ch->flags & CHN_F_TRIGGERED) { 96193d5719SCameron Grant if (ch->flags & CHN_F_MAPPED) 97193d5719SCameron Grant sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft)); 98285648f9SCameron Grant cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft); 99285648f9SCameron Grant vchan_mix_s16(dst, tmp, cnt / 2); 100285648f9SCameron Grant } 10149c5e6e2SCameron Grant } 102285648f9SCameron Grant 103285648f9SCameron Grant return count; 104285648f9SCameron Grant } 105285648f9SCameron Grant 106285648f9SCameron Grant static struct pcm_feederdesc feeder_vchan_s16_desc[] = { 107285648f9SCameron Grant {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 108285648f9SCameron Grant {0}, 109285648f9SCameron Grant }; 110285648f9SCameron Grant static kobj_method_t feeder_vchan_s16_methods[] = { 111285648f9SCameron Grant KOBJMETHOD(feeder_feed, feed_vchan_s16), 112285648f9SCameron Grant { 0, 0 } 113285648f9SCameron Grant }; 114285648f9SCameron Grant FEEDER_DECLARE(feeder_vchan_s16, 2, NULL); 115285648f9SCameron Grant 116285648f9SCameron Grant /************************************************************/ 117285648f9SCameron Grant 118285648f9SCameron Grant static void * 119285648f9SCameron Grant vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 120285648f9SCameron Grant { 121285648f9SCameron Grant struct vchinfo *ch; 122285648f9SCameron Grant struct pcm_channel *parent = devinfo; 123285648f9SCameron Grant 124285648f9SCameron Grant KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction")); 125a163d034SWarner Losh ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 126285648f9SCameron Grant ch->parent = parent; 127285648f9SCameron Grant ch->channel = c; 128285648f9SCameron Grant ch->fmt = AFMT_U8; 129285648f9SCameron Grant ch->spd = DSP_DEFAULT_SPEED; 130285648f9SCameron Grant ch->blksz = 2048; 131285648f9SCameron Grant 132285648f9SCameron Grant c->flags |= CHN_F_VIRTUAL; 133285648f9SCameron Grant 134285648f9SCameron Grant return ch; 135285648f9SCameron Grant } 136285648f9SCameron Grant 137285648f9SCameron Grant static int 138285648f9SCameron Grant vchan_free(kobj_t obj, void *data) 139285648f9SCameron Grant { 140285648f9SCameron Grant return 0; 141285648f9SCameron Grant } 142285648f9SCameron Grant 143285648f9SCameron Grant static int 144285648f9SCameron Grant vchan_setformat(kobj_t obj, void *data, u_int32_t format) 145285648f9SCameron Grant { 146285648f9SCameron Grant struct vchinfo *ch = data; 147285648f9SCameron Grant struct pcm_channel *parent = ch->parent; 148285648f9SCameron Grant 149285648f9SCameron Grant ch->fmt = format; 15049c5e6e2SCameron Grant ch->bps = 1; 15149c5e6e2SCameron Grant ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; 15249c5e6e2SCameron Grant ch->bps <<= (ch->fmt & AFMT_16BIT)? 1 : 0; 15349c5e6e2SCameron Grant ch->bps <<= (ch->fmt & AFMT_32BIT)? 2 : 0; 154285648f9SCameron Grant chn_notify(parent, CHN_N_FORMAT); 155285648f9SCameron Grant return 0; 156285648f9SCameron Grant } 157285648f9SCameron Grant 158285648f9SCameron Grant static int 159285648f9SCameron Grant vchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 160285648f9SCameron Grant { 161285648f9SCameron Grant struct vchinfo *ch = data; 162285648f9SCameron Grant struct pcm_channel *parent = ch->parent; 163285648f9SCameron Grant 164285648f9SCameron Grant ch->spd = speed; 165285648f9SCameron Grant chn_notify(parent, CHN_N_RATE); 166285648f9SCameron Grant return speed; 167285648f9SCameron Grant } 168285648f9SCameron Grant 169285648f9SCameron Grant static int 170285648f9SCameron Grant vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 171285648f9SCameron Grant { 172285648f9SCameron Grant struct vchinfo *ch = data; 173285648f9SCameron Grant struct pcm_channel *parent = ch->parent; 17449c5e6e2SCameron Grant int prate, crate; 175285648f9SCameron Grant 176285648f9SCameron Grant ch->blksz = blocksize; 177285648f9SCameron Grant chn_notify(parent, CHN_N_BLOCKSIZE); 17849c5e6e2SCameron Grant 17949c5e6e2SCameron Grant crate = ch->spd * ch->bps; 18049c5e6e2SCameron Grant prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard); 18149c5e6e2SCameron Grant blocksize = sndbuf_getblksz(parent->bufhard); 18249c5e6e2SCameron Grant blocksize *= prate; 18349c5e6e2SCameron Grant blocksize /= crate; 18449c5e6e2SCameron Grant 185285648f9SCameron Grant return blocksize; 186285648f9SCameron Grant } 187285648f9SCameron Grant 188285648f9SCameron Grant static int 189285648f9SCameron Grant vchan_trigger(kobj_t obj, void *data, int go) 190285648f9SCameron Grant { 191285648f9SCameron Grant struct vchinfo *ch = data; 192285648f9SCameron Grant struct pcm_channel *parent = ch->parent; 193285648f9SCameron Grant 194285648f9SCameron Grant if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 195285648f9SCameron Grant return 0; 196285648f9SCameron Grant 197285648f9SCameron Grant ch->run = (go == PCMTRIG_START)? 1 : 0; 198285648f9SCameron Grant chn_notify(parent, CHN_N_TRIGGER); 199285648f9SCameron Grant 200285648f9SCameron Grant return 0; 201285648f9SCameron Grant } 202285648f9SCameron Grant 203285648f9SCameron Grant static struct pcmchan_caps * 204285648f9SCameron Grant vchan_getcaps(kobj_t obj, void *data) 205285648f9SCameron Grant { 206285648f9SCameron Grant struct vchinfo *ch = data; 207285648f9SCameron Grant 208285648f9SCameron Grant ch->caps.minspeed = sndbuf_getspd(ch->parent->bufhard); 209285648f9SCameron Grant ch->caps.maxspeed = ch->caps.minspeed; 210285648f9SCameron Grant ch->caps.fmtlist = vchan_fmt; 211285648f9SCameron Grant ch->caps.caps = 0; 212285648f9SCameron Grant 213285648f9SCameron Grant return &ch->caps; 214285648f9SCameron Grant } 215285648f9SCameron Grant 216285648f9SCameron Grant static kobj_method_t vchan_methods[] = { 217285648f9SCameron Grant KOBJMETHOD(channel_init, vchan_init), 218285648f9SCameron Grant KOBJMETHOD(channel_free, vchan_free), 219285648f9SCameron Grant KOBJMETHOD(channel_setformat, vchan_setformat), 220285648f9SCameron Grant KOBJMETHOD(channel_setspeed, vchan_setspeed), 221285648f9SCameron Grant KOBJMETHOD(channel_setblocksize, vchan_setblocksize), 222285648f9SCameron Grant KOBJMETHOD(channel_trigger, vchan_trigger), 223285648f9SCameron Grant KOBJMETHOD(channel_getcaps, vchan_getcaps), 224285648f9SCameron Grant { 0, 0 } 225285648f9SCameron Grant }; 226285648f9SCameron Grant CHANNEL_DECLARE(vchan); 227285648f9SCameron Grant 228285648f9SCameron Grant /* virtual channel interface */ 229285648f9SCameron Grant 230285648f9SCameron Grant int 231285648f9SCameron Grant vchan_create(struct pcm_channel *parent) 232285648f9SCameron Grant { 233285648f9SCameron Grant struct snddev_info *d = parent->parentsnddev; 234285648f9SCameron Grant struct pcmchan_children *pce; 235285648f9SCameron Grant struct pcm_channel *child; 236285648f9SCameron Grant int err, first; 237285648f9SCameron Grant 238a163d034SWarner Losh pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 23949c5e6e2SCameron Grant if (!pce) { 240285648f9SCameron Grant return ENOMEM; 24149c5e6e2SCameron Grant } 242285648f9SCameron Grant 243285648f9SCameron Grant /* create a new playback channel */ 244285648f9SCameron Grant child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 245285648f9SCameron Grant if (!child) { 246285648f9SCameron Grant free(pce, M_DEVBUF); 247285648f9SCameron Grant return ENODEV; 248285648f9SCameron Grant } 249285648f9SCameron Grant 25067beb5a5SCameron Grant CHN_LOCK(parent); 25167beb5a5SCameron Grant if (!(parent->flags & CHN_F_BUSY)) { 25267beb5a5SCameron Grant CHN_UNLOCK(parent); 25367beb5a5SCameron Grant return EBUSY; 25467beb5a5SCameron Grant } 25567beb5a5SCameron Grant 256285648f9SCameron Grant first = SLIST_EMPTY(&parent->children); 257285648f9SCameron Grant /* add us to our parent channel's children */ 258285648f9SCameron Grant pce->channel = child; 259285648f9SCameron Grant SLIST_INSERT_HEAD(&parent->children, pce, link); 26049c5e6e2SCameron Grant CHN_UNLOCK(parent); 261285648f9SCameron Grant 262285648f9SCameron Grant /* add us to our grandparent's channel list */ 263f637a36cSCameron Grant err = pcm_chn_add(d, child, !first); 264285648f9SCameron Grant if (err) { 265285648f9SCameron Grant pcm_chn_destroy(child); 266285648f9SCameron Grant free(pce, M_DEVBUF); 267285648f9SCameron Grant } 268285648f9SCameron Grant 26967beb5a5SCameron Grant /* XXX gross ugly hack, murder death kill */ 270285648f9SCameron Grant if (first && !err) { 271285648f9SCameron Grant err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); 272285648f9SCameron Grant if (err) 273285648f9SCameron Grant printf("chn_reset: %d\n", err); 274285648f9SCameron Grant err = chn_setspeed(parent, 44100); 275285648f9SCameron Grant if (err) 276285648f9SCameron Grant printf("chn_setspeed: %d\n", err); 277285648f9SCameron Grant } 278285648f9SCameron Grant 279285648f9SCameron Grant return err; 280285648f9SCameron Grant } 281285648f9SCameron Grant 282285648f9SCameron Grant int 283285648f9SCameron Grant vchan_destroy(struct pcm_channel *c) 284285648f9SCameron Grant { 285285648f9SCameron Grant struct pcm_channel *parent = c->parentchannel; 286285648f9SCameron Grant struct snddev_info *d = parent->parentsnddev; 287285648f9SCameron Grant struct pcmchan_children *pce; 288f637a36cSCameron Grant int err, last; 289285648f9SCameron Grant 29049c5e6e2SCameron Grant CHN_LOCK(parent); 29149c5e6e2SCameron Grant if (!(parent->flags & CHN_F_BUSY)) { 29249c5e6e2SCameron Grant CHN_UNLOCK(parent); 29349c5e6e2SCameron Grant return EBUSY; 29449c5e6e2SCameron Grant } 29549c5e6e2SCameron Grant if (SLIST_EMPTY(&parent->children)) { 29649c5e6e2SCameron Grant CHN_UNLOCK(parent); 29749c5e6e2SCameron Grant return EINVAL; 29849c5e6e2SCameron Grant } 29949c5e6e2SCameron Grant 300285648f9SCameron Grant /* remove us from our parent's children list */ 301285648f9SCameron Grant SLIST_FOREACH(pce, &parent->children, link) { 302285648f9SCameron Grant if (pce->channel == c) 303285648f9SCameron Grant goto gotch; 304285648f9SCameron Grant } 30549c5e6e2SCameron Grant CHN_UNLOCK(parent); 306285648f9SCameron Grant return EINVAL; 307285648f9SCameron Grant gotch: 308285648f9SCameron Grant SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 309285648f9SCameron Grant free(pce, M_DEVBUF); 310285648f9SCameron Grant 311f637a36cSCameron Grant last = SLIST_EMPTY(&parent->children); 312f637a36cSCameron Grant if (last) 313285648f9SCameron Grant parent->flags &= ~CHN_F_BUSY; 314f637a36cSCameron Grant 315f637a36cSCameron Grant /* remove us from our grantparent's channel list */ 316f637a36cSCameron Grant err = pcm_chn_remove(d, c, !last); 317f637a36cSCameron Grant if (err) 318f637a36cSCameron Grant return err; 319f637a36cSCameron Grant 32049c5e6e2SCameron Grant CHN_UNLOCK(parent); 321285648f9SCameron Grant /* destroy ourselves */ 322285648f9SCameron Grant err = pcm_chn_destroy(c); 323285648f9SCameron Grant 324285648f9SCameron Grant return err; 325285648f9SCameron Grant } 326285648f9SCameron Grant 327285648f9SCameron Grant int 32867b1dce3SCameron Grant vchan_initsys(device_t dev) 329285648f9SCameron Grant { 330285648f9SCameron Grant #ifdef SND_DYNSYSCTL 33167b1dce3SCameron Grant struct snddev_info *d; 33267b1dce3SCameron Grant 33367b1dce3SCameron Grant d = device_get_softc(dev); 33467b1dce3SCameron Grant SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 335285648f9SCameron Grant OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 33664d85ef7SMark Murray sysctl_hw_snd_vchans, "I", ""); 337285648f9SCameron Grant #endif 33867b1dce3SCameron Grant 339285648f9SCameron Grant return 0; 340285648f9SCameron Grant } 341285648f9SCameron Grant 342285648f9SCameron Grant 343