1285648f9SCameron Grant /* 2285648f9SCameron Grant * Copyright (c) 2001 Cameron Grant <gandalf@vilnya.demon.co.uk> 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_S16_LE, 41285648f9SCameron Grant AFMT_STEREO | AFMT_S16_LE, 42285648f9SCameron Grant 0 43285648f9SCameron Grant }; 44285648f9SCameron Grant 45285648f9SCameron Grant static int 46285648f9SCameron Grant vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count) 47285648f9SCameron Grant { 48285648f9SCameron Grant /* 49285648f9SCameron Grant * to is the output buffer, tmp is the input buffer 50285648f9SCameron Grant * count is the number of 16bit samples to mix 51285648f9SCameron Grant */ 52285648f9SCameron Grant int i; 53285648f9SCameron Grant int x; 54285648f9SCameron Grant 55285648f9SCameron Grant for(i = 0; i < count; i++) { 56285648f9SCameron Grant x = to[i]; 57285648f9SCameron Grant x += tmp[i]; 58285648f9SCameron Grant if (x < -32768) { 59285648f9SCameron Grant /* printf("%d + %d = %d (u)\n", to[i], tmp[i], x); */ 60285648f9SCameron Grant x = -32768; 61285648f9SCameron Grant } 62285648f9SCameron Grant if (x > 32767) { 63285648f9SCameron Grant /* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */ 64285648f9SCameron Grant x = 32767; 65285648f9SCameron Grant } 66285648f9SCameron Grant to[i] = x & 0x0000ffff; 67285648f9SCameron Grant } 68285648f9SCameron Grant return 0; 69285648f9SCameron Grant } 70285648f9SCameron Grant 71285648f9SCameron Grant static int 72285648f9SCameron Grant feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) 73285648f9SCameron Grant { 74285648f9SCameron Grant /* we're going to abuse things a bit */ 75285648f9SCameron Grant struct snd_dbuf *src = source; 76285648f9SCameron Grant struct pcmchan_children *cce; 77285648f9SCameron Grant struct pcm_channel *ch; 78285648f9SCameron Grant int16_t *tmp, *dst; 79285648f9SCameron Grant unsigned int cnt; 80285648f9SCameron Grant 81285648f9SCameron Grant KASSERT(sndbuf_getsize(src) >= count, ("bad bufsize")); 82285648f9SCameron Grant count &= ~1; 83285648f9SCameron Grant bzero(b, count); 84285648f9SCameron Grant 85285648f9SCameron Grant /* 86285648f9SCameron Grant * we are going to use our source as a temporary buffer since it's 87285648f9SCameron Grant * got no other purpose. we obtain our data by traversing the channel 88285648f9SCameron Grant * list of children and calling vchan_mix_* to mix count bytes from each 89285648f9SCameron Grant * into our destination buffer, b 90285648f9SCameron Grant */ 91285648f9SCameron Grant dst = (int16_t *)b; 92285648f9SCameron Grant tmp = (int16_t *)sndbuf_getbuf(src); 93285648f9SCameron Grant bzero(tmp, count); 94285648f9SCameron Grant SLIST_FOREACH(cce, &c->children, link) { 95285648f9SCameron Grant ch = cce->channel; 9649c5e6e2SCameron Grant if (ch->flags & CHN_F_TRIGGERED) { 97285648f9SCameron Grant cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft); 98285648f9SCameron Grant vchan_mix_s16(dst, tmp, cnt / 2); 99285648f9SCameron Grant } 10049c5e6e2SCameron Grant } 101285648f9SCameron Grant 102285648f9SCameron Grant return count; 103285648f9SCameron Grant } 104285648f9SCameron Grant 105285648f9SCameron Grant static struct pcm_feederdesc feeder_vchan_s16_desc[] = { 106285648f9SCameron Grant {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0}, 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")); 125285648f9SCameron Grant 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 23849c5e6e2SCameron Grant CHN_LOCK(parent); 23949c5e6e2SCameron Grant if (!(parent->flags & CHN_F_BUSY)) { 24049c5e6e2SCameron Grant CHN_UNLOCK(parent); 241285648f9SCameron Grant return EBUSY; 24249c5e6e2SCameron Grant } 243285648f9SCameron Grant 244285648f9SCameron Grant pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 24549c5e6e2SCameron Grant if (!pce) { 24649c5e6e2SCameron Grant CHN_UNLOCK(parent); 247285648f9SCameron Grant return ENOMEM; 24849c5e6e2SCameron Grant } 249285648f9SCameron Grant 250285648f9SCameron Grant /* create a new playback channel */ 251285648f9SCameron Grant child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 252285648f9SCameron Grant if (!child) { 253285648f9SCameron Grant free(pce, M_DEVBUF); 25449c5e6e2SCameron Grant CHN_UNLOCK(parent); 255285648f9SCameron Grant return ENODEV; 256285648f9SCameron Grant } 257285648f9SCameron Grant 258285648f9SCameron Grant first = SLIST_EMPTY(&parent->children); 259285648f9SCameron Grant /* add us to our parent channel's children */ 260285648f9SCameron Grant pce->channel = child; 261285648f9SCameron Grant SLIST_INSERT_HEAD(&parent->children, pce, link); 26249c5e6e2SCameron Grant CHN_UNLOCK(parent); 263285648f9SCameron Grant 264285648f9SCameron Grant /* add us to our grandparent's channel list */ 265f637a36cSCameron Grant err = pcm_chn_add(d, child, !first); 266285648f9SCameron Grant if (err) { 267285648f9SCameron Grant pcm_chn_destroy(child); 268285648f9SCameron Grant free(pce, M_DEVBUF); 269285648f9SCameron Grant } 270285648f9SCameron Grant 271285648f9SCameron Grant /* XXX gross ugly hack, kill murder death */ 272285648f9SCameron Grant if (first && !err) { 273285648f9SCameron Grant err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); 274285648f9SCameron Grant if (err) 275285648f9SCameron Grant printf("chn_reset: %d\n", err); 276285648f9SCameron Grant err = chn_setspeed(parent, 44100); 277285648f9SCameron Grant if (err) 278285648f9SCameron Grant printf("chn_setspeed: %d\n", err); 279285648f9SCameron Grant } 280285648f9SCameron Grant 281285648f9SCameron Grant return err; 282285648f9SCameron Grant } 283285648f9SCameron Grant 284285648f9SCameron Grant int 285285648f9SCameron Grant vchan_destroy(struct pcm_channel *c) 286285648f9SCameron Grant { 287285648f9SCameron Grant struct pcm_channel *parent = c->parentchannel; 288285648f9SCameron Grant struct snddev_info *d = parent->parentsnddev; 289285648f9SCameron Grant struct pcmchan_children *pce; 290f637a36cSCameron Grant int err, last; 291285648f9SCameron Grant 29249c5e6e2SCameron Grant CHN_LOCK(parent); 29349c5e6e2SCameron Grant if (!(parent->flags & CHN_F_BUSY)) { 29449c5e6e2SCameron Grant CHN_UNLOCK(parent); 29549c5e6e2SCameron Grant return EBUSY; 29649c5e6e2SCameron Grant } 29749c5e6e2SCameron Grant if (SLIST_EMPTY(&parent->children)) { 29849c5e6e2SCameron Grant CHN_UNLOCK(parent); 29949c5e6e2SCameron Grant return EINVAL; 30049c5e6e2SCameron Grant } 30149c5e6e2SCameron Grant 302285648f9SCameron Grant /* remove us from our parent's children list */ 303285648f9SCameron Grant SLIST_FOREACH(pce, &parent->children, link) { 304285648f9SCameron Grant if (pce->channel == c) 305285648f9SCameron Grant goto gotch; 306285648f9SCameron Grant } 30749c5e6e2SCameron Grant CHN_UNLOCK(parent); 308285648f9SCameron Grant return EINVAL; 309285648f9SCameron Grant gotch: 310285648f9SCameron Grant SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 311285648f9SCameron Grant free(pce, M_DEVBUF); 312285648f9SCameron Grant 313f637a36cSCameron Grant last = SLIST_EMPTY(&parent->children); 314f637a36cSCameron Grant if (last) 315285648f9SCameron Grant parent->flags &= ~CHN_F_BUSY; 316f637a36cSCameron Grant 317f637a36cSCameron Grant /* remove us from our grantparent's channel list */ 318f637a36cSCameron Grant err = pcm_chn_remove(d, c, !last); 319f637a36cSCameron Grant if (err) 320f637a36cSCameron Grant return err; 321f637a36cSCameron Grant 32249c5e6e2SCameron Grant CHN_UNLOCK(parent); 323285648f9SCameron Grant /* destroy ourselves */ 324285648f9SCameron Grant err = pcm_chn_destroy(c); 325285648f9SCameron Grant 326285648f9SCameron Grant return err; 327285648f9SCameron Grant } 328285648f9SCameron Grant 329285648f9SCameron Grant int 33067b1dce3SCameron Grant vchan_initsys(device_t dev) 331285648f9SCameron Grant { 332285648f9SCameron Grant #ifdef SND_DYNSYSCTL 33367b1dce3SCameron Grant struct snddev_info *d; 33467b1dce3SCameron Grant 33567b1dce3SCameron Grant d = device_get_softc(dev); 33667b1dce3SCameron Grant SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 337285648f9SCameron Grant OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 338285648f9SCameron Grant sysctl_hw_snd_vchans, "I", "") 339285648f9SCameron Grant #endif 34067b1dce3SCameron Grant 341285648f9SCameron Grant return 0; 342285648f9SCameron Grant } 343285648f9SCameron Grant 344285648f9SCameron Grant 345