1 /* 2 * Copyright (c) 2001 Cameron Grant <cg@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <dev/sound/pcm/sound.h> 28 #include <dev/sound/pcm/vchan.h> 29 #include "feeder_if.h" 30 31 SND_DECLARE_FILE("$FreeBSD$"); 32 33 struct vchinfo { 34 u_int32_t spd, fmt, blksz, bps, run; 35 struct pcm_channel *channel, *parent; 36 struct pcmchan_caps caps; 37 }; 38 39 static u_int32_t vchan_fmt[] = { 40 AFMT_STEREO | AFMT_S16_LE, 41 0 42 }; 43 44 static int 45 vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count) 46 { 47 /* 48 * to is the output buffer, tmp is the input buffer 49 * count is the number of 16bit samples to mix 50 */ 51 int i; 52 int x; 53 54 for(i = 0; i < count; i++) { 55 x = to[i]; 56 x += tmp[i]; 57 if (x < -32768) { 58 /* printf("%d + %d = %d (u)\n", to[i], tmp[i], x); */ 59 x = -32768; 60 } 61 if (x > 32767) { 62 /* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */ 63 x = 32767; 64 } 65 to[i] = x & 0x0000ffff; 66 } 67 return 0; 68 } 69 70 static int 71 feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) 72 { 73 /* we're going to abuse things a bit */ 74 struct snd_dbuf *src = source; 75 struct pcmchan_children *cce; 76 struct pcm_channel *ch; 77 int16_t *tmp, *dst; 78 unsigned int cnt; 79 80 KASSERT(sndbuf_getsize(src) >= count, ("bad bufsize")); 81 count &= ~1; 82 bzero(b, count); 83 84 /* 85 * we are going to use our source as a temporary buffer since it's 86 * got no other purpose. we obtain our data by traversing the channel 87 * list of children and calling vchan_mix_* to mix count bytes from each 88 * into our destination buffer, b 89 */ 90 dst = (int16_t *)b; 91 tmp = (int16_t *)sndbuf_getbuf(src); 92 bzero(tmp, count); 93 SLIST_FOREACH(cce, &c->children, link) { 94 ch = cce->channel; 95 if (ch->flags & CHN_F_TRIGGERED) { 96 if (ch->flags & CHN_F_MAPPED) 97 sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft)); 98 cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft); 99 vchan_mix_s16(dst, tmp, cnt / 2); 100 } 101 } 102 103 return count; 104 } 105 106 static struct pcm_feederdesc feeder_vchan_s16_desc[] = { 107 {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 108 {0}, 109 }; 110 static kobj_method_t feeder_vchan_s16_methods[] = { 111 KOBJMETHOD(feeder_feed, feed_vchan_s16), 112 { 0, 0 } 113 }; 114 FEEDER_DECLARE(feeder_vchan_s16, 2, NULL); 115 116 /************************************************************/ 117 118 static void * 119 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 120 { 121 struct vchinfo *ch; 122 struct pcm_channel *parent = devinfo; 123 124 KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction")); 125 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 126 ch->parent = parent; 127 ch->channel = c; 128 ch->fmt = AFMT_U8; 129 ch->spd = DSP_DEFAULT_SPEED; 130 ch->blksz = 2048; 131 132 c->flags |= CHN_F_VIRTUAL; 133 134 return ch; 135 } 136 137 static int 138 vchan_free(kobj_t obj, void *data) 139 { 140 return 0; 141 } 142 143 static int 144 vchan_setformat(kobj_t obj, void *data, u_int32_t format) 145 { 146 struct vchinfo *ch = data; 147 struct pcm_channel *parent = ch->parent; 148 149 ch->fmt = format; 150 ch->bps = 1; 151 ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; 152 ch->bps <<= (ch->fmt & AFMT_16BIT)? 1 : 0; 153 ch->bps <<= (ch->fmt & AFMT_32BIT)? 2 : 0; 154 chn_notify(parent, CHN_N_FORMAT); 155 return 0; 156 } 157 158 static int 159 vchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 160 { 161 struct vchinfo *ch = data; 162 struct pcm_channel *parent = ch->parent; 163 164 ch->spd = speed; 165 chn_notify(parent, CHN_N_RATE); 166 return speed; 167 } 168 169 static int 170 vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 171 { 172 struct vchinfo *ch = data; 173 struct pcm_channel *parent = ch->parent; 174 int prate, crate; 175 176 ch->blksz = blocksize; 177 chn_notify(parent, CHN_N_BLOCKSIZE); 178 179 crate = ch->spd * ch->bps; 180 prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard); 181 blocksize = sndbuf_getblksz(parent->bufhard); 182 blocksize *= prate; 183 blocksize /= crate; 184 185 return blocksize; 186 } 187 188 static int 189 vchan_trigger(kobj_t obj, void *data, int go) 190 { 191 struct vchinfo *ch = data; 192 struct pcm_channel *parent = ch->parent; 193 194 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 195 return 0; 196 197 ch->run = (go == PCMTRIG_START)? 1 : 0; 198 chn_notify(parent, CHN_N_TRIGGER); 199 200 return 0; 201 } 202 203 static struct pcmchan_caps * 204 vchan_getcaps(kobj_t obj, void *data) 205 { 206 struct vchinfo *ch = data; 207 208 ch->caps.minspeed = sndbuf_getspd(ch->parent->bufhard); 209 ch->caps.maxspeed = ch->caps.minspeed; 210 ch->caps.fmtlist = vchan_fmt; 211 ch->caps.caps = 0; 212 213 return &ch->caps; 214 } 215 216 static kobj_method_t vchan_methods[] = { 217 KOBJMETHOD(channel_init, vchan_init), 218 KOBJMETHOD(channel_free, vchan_free), 219 KOBJMETHOD(channel_setformat, vchan_setformat), 220 KOBJMETHOD(channel_setspeed, vchan_setspeed), 221 KOBJMETHOD(channel_setblocksize, vchan_setblocksize), 222 KOBJMETHOD(channel_trigger, vchan_trigger), 223 KOBJMETHOD(channel_getcaps, vchan_getcaps), 224 { 0, 0 } 225 }; 226 CHANNEL_DECLARE(vchan); 227 228 /* virtual channel interface */ 229 230 int 231 vchan_create(struct pcm_channel *parent) 232 { 233 struct snddev_info *d = parent->parentsnddev; 234 struct pcmchan_children *pce; 235 struct pcm_channel *child; 236 int err, first; 237 238 pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 239 if (!pce) { 240 return ENOMEM; 241 } 242 243 /* create a new playback channel */ 244 child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 245 if (!child) { 246 free(pce, M_DEVBUF); 247 return ENODEV; 248 } 249 250 CHN_LOCK(parent); 251 if (!(parent->flags & CHN_F_BUSY)) { 252 CHN_UNLOCK(parent); 253 return EBUSY; 254 } 255 256 first = SLIST_EMPTY(&parent->children); 257 /* add us to our parent channel's children */ 258 pce->channel = child; 259 SLIST_INSERT_HEAD(&parent->children, pce, link); 260 CHN_UNLOCK(parent); 261 262 /* add us to our grandparent's channel list */ 263 err = pcm_chn_add(d, child, !first); 264 if (err) { 265 pcm_chn_destroy(child); 266 free(pce, M_DEVBUF); 267 } 268 269 /* XXX gross ugly hack, murder death kill */ 270 if (first && !err) { 271 err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); 272 if (err) 273 printf("chn_reset: %d\n", err); 274 err = chn_setspeed(parent, 44100); 275 if (err) 276 printf("chn_setspeed: %d\n", err); 277 } 278 279 return err; 280 } 281 282 int 283 vchan_destroy(struct pcm_channel *c) 284 { 285 struct pcm_channel *parent = c->parentchannel; 286 struct snddev_info *d = parent->parentsnddev; 287 struct pcmchan_children *pce; 288 int err, last; 289 290 CHN_LOCK(parent); 291 if (!(parent->flags & CHN_F_BUSY)) { 292 CHN_UNLOCK(parent); 293 return EBUSY; 294 } 295 if (SLIST_EMPTY(&parent->children)) { 296 CHN_UNLOCK(parent); 297 return EINVAL; 298 } 299 300 /* remove us from our parent's children list */ 301 SLIST_FOREACH(pce, &parent->children, link) { 302 if (pce->channel == c) 303 goto gotch; 304 } 305 CHN_UNLOCK(parent); 306 return EINVAL; 307 gotch: 308 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 309 free(pce, M_DEVBUF); 310 311 last = SLIST_EMPTY(&parent->children); 312 if (last) 313 parent->flags &= ~CHN_F_BUSY; 314 315 /* remove us from our grantparent's channel list */ 316 err = pcm_chn_remove(d, c, !last); 317 if (err) 318 return err; 319 320 CHN_UNLOCK(parent); 321 /* destroy ourselves */ 322 err = pcm_chn_destroy(c); 323 324 return err; 325 } 326 327 int 328 vchan_initsys(device_t dev) 329 { 330 #ifdef SND_DYNSYSCTL 331 struct snddev_info *d; 332 333 d = device_get_softc(dev); 334 SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 335 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 336 sysctl_hw_snd_vchans, "I", ""); 337 #endif 338 339 return 0; 340 } 341 342 343