1 /*- 2 * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> 3 * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * Almost entirely rewritten to add multi-format/channels mixing support. 28 * 29 */ 30 31 #include <dev/sound/pcm/sound.h> 32 #include <dev/sound/pcm/vchan.h> 33 #include "feeder_if.h" 34 35 SND_DECLARE_FILE("$FreeBSD$"); 36 37 MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder"); 38 39 /* 40 * Default speed / format 41 */ 42 #define VCHAN_DEFAULT_SPEED 48000 43 #define VCHAN_DEFAULT_AFMT (AFMT_S16_LE | AFMT_STEREO) 44 #define VCHAN_DEFAULT_STRFMT "s16le" 45 46 typedef uint32_t (*feed_vchan_mixer)(uint8_t *, uint8_t *, uint32_t); 47 48 struct vchinfo { 49 uint32_t spd, fmt, fmts[2], blksz, bps, run; 50 struct pcm_channel *channel, *parent; 51 struct pcmchan_caps caps; 52 }; 53 54 /* support everything (mono / stereo), except a-law / mu-law */ 55 static struct afmtstr_table vchan_supported_fmts[] = { 56 { "u8", AFMT_U8 }, { "s8", AFMT_S8 }, 57 { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, 58 { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, 59 { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE }, 60 { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE }, 61 { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE }, 62 { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE }, 63 { NULL, 0 }, 64 }; 65 66 /* alias table, shorter. */ 67 static const struct { 68 char *alias, *fmtstr; 69 } vchan_fmtstralias[] = { 70 { "8", "u8" }, { "16", "s16le" }, 71 { "24", "s24le" }, { "32", "s32le" }, 72 { NULL, NULL }, 73 }; 74 75 #define vchan_valid_format(fmt) \ 76 afmt2afmtstr(vchan_supported_fmts, fmt, NULL, 0, 0, \ 77 AFMTSTR_STEREO_RETURN) 78 #define vchan_valid_strformat(strfmt) \ 79 afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN); 80 81 /* 82 * Need specialized WRITE macros since 32bit might involved saturation 83 * if calculation is done within 32bit arithmetic. 84 */ 85 #define VCHAN_PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) 86 #define VCHAN_PCM_WRITE_S16_LE(b8, val) PCM_WRITE_S16_LE(b8, val) 87 #define VCHAN_PCM_WRITE_S24_LE(b8, val) PCM_WRITE_S24_LE(b8, val) 88 #define VCHAN_PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) 89 #define VCHAN_PCM_WRITE_S16_BE(b8, val) PCM_WRITE_S16_BE(b8, val) 90 #define VCHAN_PCM_WRITE_S24_BE(b8, val) PCM_WRITE_S24_BE(b8, val) 91 #define VCHAN_PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) 92 #define VCHAN_PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) 93 #define VCHAN_PCM_WRITE_U16_LE(b8, val) PCM_WRITE_U16_LE(b8, val) 94 #define VCHAN_PCM_WRITE_U24_LE(b8, val) PCM_WRITE_U24_LE(b8, val) 95 #define VCHAN_PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) 96 #define VCHAN_PCM_WRITE_U16_BE(b8, val) PCM_WRITE_U16_BE(b8, val) 97 #define VCHAN_PCM_WRITE_U24_BE(b8, val) PCM_WRITE_U24_BE(b8, val) 98 #define VCHAN_PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) 99 100 #define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ 101 static uint32_t \ 102 feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(uint8_t *to, uint8_t *tmp, \ 103 uint32_t count) \ 104 { \ 105 int32_t x, y; \ 106 VCHAN_INTCAST z; \ 107 int i; \ 108 \ 109 i = count; \ 110 tmp += i; \ 111 to += i; \ 112 \ 113 do { \ 114 tmp -= PCM_##FMTBIT##_BPS; \ 115 to -= PCM_##FMTBIT##_BPS; \ 116 i -= PCM_##FMTBIT##_BPS; \ 117 x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp); \ 118 y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to); \ 119 z = (VCHAN_INTCAST)x + y; \ 120 x = PCM_CLAMP_##SIGN##FMTBIT(z); \ 121 VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x); \ 122 } while (i != 0); \ 123 \ 124 return count; \ 125 } 126 127 FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne) 128 FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le) 129 FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le) 130 FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le) 131 FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be) 132 FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be) 133 FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be) 134 FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne) 135 FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le) 136 FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le) 137 FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le) 138 FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be) 139 FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be) 140 FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be) 141 142 struct feed_vchan_info { 143 uint32_t format; 144 int bps; 145 feed_vchan_mixer mix; 146 }; 147 148 static struct feed_vchan_info feed_vchan_info_tbl[] = { 149 { AFMT_S8, PCM_8_BPS, feed_vchan_mix_s8ne }, 150 { AFMT_S16_LE, PCM_16_BPS, feed_vchan_mix_s16le }, 151 { AFMT_S24_LE, PCM_24_BPS, feed_vchan_mix_s24le }, 152 { AFMT_S32_LE, PCM_32_BPS, feed_vchan_mix_s32le }, 153 { AFMT_S16_BE, PCM_16_BPS, feed_vchan_mix_s16be }, 154 { AFMT_S24_BE, PCM_24_BPS, feed_vchan_mix_s24be }, 155 { AFMT_S32_BE, PCM_32_BPS, feed_vchan_mix_s32be }, 156 { AFMT_U8, PCM_8_BPS, feed_vchan_mix_u8ne }, 157 { AFMT_U16_LE, PCM_16_BPS, feed_vchan_mix_u16le }, 158 { AFMT_U24_LE, PCM_24_BPS, feed_vchan_mix_u24le }, 159 { AFMT_U32_LE, PCM_32_BPS, feed_vchan_mix_u32le }, 160 { AFMT_U16_BE, PCM_16_BPS, feed_vchan_mix_u16be }, 161 { AFMT_U24_BE, PCM_24_BPS, feed_vchan_mix_u24be }, 162 { AFMT_U32_BE, PCM_32_BPS, feed_vchan_mix_u32be }, 163 }; 164 165 #define FVCHAN_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf))) 166 #define FVCHAN_INFOIDX(m) (((m) >> 4) & 0x1f) 167 #define FVCHAN_CHANNELS(m) ((m) & 0xf) 168 169 static int 170 feed_vchan_init(struct pcm_feeder *f) 171 { 172 int i, channels; 173 174 if (f->desc->out != f->desc->in) 175 return EINVAL; 176 177 channels = (f->desc->out & AFMT_STEREO) ? 2 : 1; 178 179 for (i = 0; i < sizeof(feed_vchan_info_tbl) / 180 sizeof(feed_vchan_info_tbl[0]); i++) { 181 if ((f->desc->out & ~AFMT_STEREO) == 182 feed_vchan_info_tbl[i].format) { 183 f->data = (void *)FVCHAN_DATA(i, channels); 184 return 0; 185 } 186 } 187 188 return -1; 189 } 190 191 static int 192 feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 193 uint32_t count, void *source) 194 { 195 struct feed_vchan_info *info; 196 struct snd_dbuf *src = source; 197 struct pcmchan_children *cce; 198 struct pcm_channel *ch; 199 uint32_t cnt, mcnt, rcnt, sz; 200 uint8_t *tmp; 201 202 sz = sndbuf_getsize(src); 203 if (sz < count) 204 count = sz; 205 206 info = &feed_vchan_info_tbl[FVCHAN_INFOIDX((intptr_t)f->data)]; 207 sz = info->bps * FVCHAN_CHANNELS((intptr_t)f->data); 208 count -= count % sz; 209 if (count < sz) 210 return 0; 211 212 /* 213 * we are going to use our source as a temporary buffer since it's 214 * got no other purpose. we obtain our data by traversing the channel 215 * list of children and calling vchan_mix_* to mix count bytes from 216 * each into our destination buffer, b 217 */ 218 tmp = sndbuf_getbuf(src); 219 rcnt = 0; 220 mcnt = 0; 221 222 SLIST_FOREACH(cce, &c->children, link) { 223 ch = cce->channel; 224 CHN_LOCK(ch); 225 if (!(ch->flags & CHN_F_TRIGGERED)) { 226 CHN_UNLOCK(ch); 227 continue; 228 } 229 if ((ch->flags & CHN_F_MAPPED) && !(ch->flags & CHN_F_CLOSING)) 230 sndbuf_acquire(ch->bufsoft, NULL, 231 sndbuf_getfree(ch->bufsoft)); 232 if (rcnt == 0) { 233 rcnt = FEEDER_FEED(ch->feeder, ch, b, count, 234 ch->bufsoft); 235 rcnt -= rcnt % sz; 236 mcnt = count - rcnt; 237 } else { 238 cnt = FEEDER_FEED(ch->feeder, ch, tmp, count, 239 ch->bufsoft); 240 cnt -= cnt % sz; 241 if (cnt != 0) { 242 if (mcnt != 0) { 243 memset(b + rcnt, 244 sndbuf_zerodata(f->desc->out), 245 mcnt); 246 mcnt = 0; 247 } 248 cnt = info->mix(b, tmp, cnt); 249 if (cnt > rcnt) 250 rcnt = cnt; 251 } 252 } 253 CHN_UNLOCK(ch); 254 } 255 256 if (++c->feedcount == 0) 257 c->feedcount = 2; 258 259 return rcnt; 260 } 261 262 static struct pcm_feederdesc feeder_vchan_desc[] = { 263 {FEEDER_MIXER, AFMT_S8, AFMT_S8, 0}, 264 {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0}, 265 {FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0}, 266 {FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0}, 267 {FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0}, 268 {FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0}, 269 {FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0}, 270 {FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, 271 {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 272 {FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, 273 {FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, 274 {FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, 275 {FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, 276 {FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, 277 {FEEDER_MIXER, AFMT_U8, AFMT_U8, 0}, 278 {FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0}, 279 {FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0}, 280 {FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0}, 281 {FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0}, 282 {FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0}, 283 {FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0}, 284 {FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, 285 {FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, 286 {FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, 287 {FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, 288 {FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, 289 {FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, 290 {FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, 291 {0, 0, 0, 0}, 292 }; 293 static kobj_method_t feeder_vchan_methods[] = { 294 KOBJMETHOD(feeder_init, feed_vchan_init), 295 KOBJMETHOD(feeder_feed, feed_vchan), 296 {0, 0} 297 }; 298 FEEDER_DECLARE(feeder_vchan, 2, NULL); 299 300 /************************************************************/ 301 302 static void * 303 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 304 { 305 struct vchinfo *ch; 306 struct pcm_channel *parent = devinfo; 307 308 KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction")); 309 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 310 ch->parent = parent; 311 ch->channel = c; 312 ch->fmt = AFMT_U8; 313 ch->spd = DSP_DEFAULT_SPEED; 314 ch->blksz = 2048; 315 316 c->flags |= CHN_F_VIRTUAL; 317 318 return ch; 319 } 320 321 static int 322 vchan_free(kobj_t obj, void *data) 323 { 324 free(data, M_DEVBUF); 325 return 0; 326 } 327 328 static int 329 vchan_setformat(kobj_t obj, void *data, uint32_t format) 330 { 331 struct vchinfo *ch = data; 332 struct pcm_channel *parent = ch->parent; 333 struct pcm_channel *channel = ch->channel; 334 335 ch->fmt = format; 336 ch->bps = 1; 337 ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; 338 if (ch->fmt & AFMT_16BIT) 339 ch->bps <<= 1; 340 else if (ch->fmt & AFMT_24BIT) 341 ch->bps *= 3; 342 else if (ch->fmt & AFMT_32BIT) 343 ch->bps <<= 2; 344 CHN_UNLOCK(channel); 345 chn_notify(parent, CHN_N_FORMAT); 346 CHN_LOCK(channel); 347 sndbuf_setfmt(channel->bufsoft, format); 348 return 0; 349 } 350 351 static int 352 vchan_setspeed(kobj_t obj, void *data, uint32_t speed) 353 { 354 struct vchinfo *ch = data; 355 struct pcm_channel *parent = ch->parent; 356 struct pcm_channel *channel = ch->channel; 357 358 ch->spd = speed; 359 CHN_UNLOCK(channel); 360 CHN_LOCK(parent); 361 speed = sndbuf_getspd(parent->bufsoft); 362 CHN_UNLOCK(parent); 363 CHN_LOCK(channel); 364 return speed; 365 } 366 367 static int 368 vchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) 369 { 370 struct vchinfo *ch = data; 371 struct pcm_channel *channel = ch->channel; 372 struct pcm_channel *parent = ch->parent; 373 /* struct pcm_channel *channel = ch->channel; */ 374 int prate, crate; 375 376 ch->blksz = blocksize; 377 /* CHN_UNLOCK(channel); */ 378 sndbuf_setblksz(channel->bufhard, blocksize); 379 chn_notify(parent, CHN_N_BLOCKSIZE); 380 CHN_LOCK(parent); 381 /* CHN_LOCK(channel); */ 382 383 crate = ch->spd * ch->bps; 384 prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft); 385 blocksize = sndbuf_getblksz(parent->bufsoft); 386 CHN_UNLOCK(parent); 387 blocksize *= prate; 388 blocksize /= crate; 389 blocksize += ch->bps; 390 prate = 0; 391 while (blocksize >> prate) 392 prate++; 393 blocksize = 1 << (prate - 1); 394 blocksize -= blocksize % ch->bps; 395 /* XXX screwed !@#$ */ 396 if (blocksize < ch->bps) 397 blocksize = 4096 - (4096 % ch->bps); 398 399 return blocksize; 400 } 401 402 static int 403 vchan_trigger(kobj_t obj, void *data, int go) 404 { 405 struct vchinfo *ch = data; 406 struct pcm_channel *parent = ch->parent; 407 struct pcm_channel *channel = ch->channel; 408 409 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 410 return 0; 411 412 ch->run = (go == PCMTRIG_START)? 1 : 0; 413 CHN_UNLOCK(channel); 414 chn_notify(parent, CHN_N_TRIGGER); 415 CHN_LOCK(channel); 416 417 return 0; 418 } 419 420 static struct pcmchan_caps * 421 vchan_getcaps(kobj_t obj, void *data) 422 { 423 struct vchinfo *ch = data; 424 uint32_t fmt; 425 426 ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft); 427 ch->caps.maxspeed = ch->caps.minspeed; 428 ch->caps.caps = 0; 429 ch->fmts[1] = 0; 430 fmt = sndbuf_getfmt(ch->parent->bufsoft); 431 if (fmt != vchan_valid_format(fmt)) { 432 device_printf(ch->parent->dev, 433 "%s: WARNING: invalid vchan format! (0x%08x)\n", 434 __func__, fmt); 435 fmt = VCHAN_DEFAULT_AFMT; 436 } 437 ch->fmts[0] = fmt; 438 ch->caps.fmtlist = ch->fmts; 439 440 return &ch->caps; 441 } 442 443 static kobj_method_t vchan_methods[] = { 444 KOBJMETHOD(channel_init, vchan_init), 445 KOBJMETHOD(channel_free, vchan_free), 446 KOBJMETHOD(channel_setformat, vchan_setformat), 447 KOBJMETHOD(channel_setspeed, vchan_setspeed), 448 KOBJMETHOD(channel_setblocksize, vchan_setblocksize), 449 KOBJMETHOD(channel_trigger, vchan_trigger), 450 KOBJMETHOD(channel_getcaps, vchan_getcaps), 451 {0, 0} 452 }; 453 CHANNEL_DECLARE(vchan); 454 455 /* 456 * On the fly vchan rate settings 457 */ 458 #ifdef SND_DYNSYSCTL 459 static int 460 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) 461 { 462 struct snddev_info *d; 463 struct snddev_channel *sce; 464 struct pcm_channel *c, *ch = NULL, *fake; 465 struct pcmchan_caps *caps; 466 int err = 0; 467 int newspd = 0; 468 469 d = oidp->oid_arg1; 470 if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) 471 return EINVAL; 472 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 473 pcm_inprog(d, -1); 474 return EINPROGRESS; 475 } 476 SLIST_FOREACH(sce, &d->channels, link) { 477 c = sce->channel; 478 CHN_LOCK(c); 479 if (c->direction == PCMDIR_PLAY) { 480 if (c->flags & CHN_F_VIRTUAL) { 481 /* Sanity check */ 482 if (ch != NULL && ch != c->parentchannel) { 483 CHN_UNLOCK(c); 484 pcm_inprog(d, -1); 485 return EINVAL; 486 } 487 if (req->newptr != NULL && 488 (c->flags & CHN_F_BUSY)) { 489 CHN_UNLOCK(c); 490 pcm_inprog(d, -1); 491 return EBUSY; 492 } 493 } else if (c->flags & CHN_F_HAS_VCHAN) { 494 /* No way!! */ 495 if (ch != NULL) { 496 CHN_UNLOCK(c); 497 pcm_inprog(d, -1); 498 return EINVAL; 499 } 500 ch = c; 501 newspd = ch->speed; 502 } 503 } 504 CHN_UNLOCK(c); 505 } 506 if (ch == NULL) { 507 pcm_inprog(d, -1); 508 return EINVAL; 509 } 510 err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req); 511 if (err == 0 && req->newptr != NULL) { 512 if (newspd < 1 || newspd < feeder_rate_min || 513 newspd > feeder_rate_max) { 514 pcm_inprog(d, -1); 515 return EINVAL; 516 } 517 CHN_LOCK(ch); 518 if (feeder_rate_round) { 519 caps = chn_getcaps(ch); 520 if (caps == NULL || newspd < caps->minspeed || 521 newspd > caps->maxspeed) { 522 CHN_UNLOCK(ch); 523 pcm_inprog(d, -1); 524 return EINVAL; 525 } 526 } 527 if (newspd != ch->speed) { 528 err = chn_setspeed(ch, newspd); 529 /* 530 * Try to avoid FEEDER_RATE on parent channel if the 531 * requested value is not supported by the hardware. 532 */ 533 if (!err && feeder_rate_round && 534 (ch->feederflags & (1 << FEEDER_RATE))) { 535 newspd = sndbuf_getspd(ch->bufhard); 536 err = chn_setspeed(ch, newspd); 537 } 538 CHN_UNLOCK(ch); 539 if (err == 0) { 540 fake = pcm_getfakechan(d); 541 if (fake != NULL) { 542 CHN_LOCK(fake); 543 fake->speed = newspd; 544 CHN_UNLOCK(fake); 545 } 546 } 547 } else 548 CHN_UNLOCK(ch); 549 } 550 pcm_inprog(d, -1); 551 return err; 552 } 553 554 static int 555 sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) 556 { 557 struct snddev_info *d; 558 struct snddev_channel *sce; 559 struct pcm_channel *c, *ch = NULL, *fake; 560 uint32_t newfmt, spd; 561 char fmtstr[AFMTSTR_MAXSZ]; 562 int err = 0, i; 563 564 d = oidp->oid_arg1; 565 if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) 566 return EINVAL; 567 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 568 pcm_inprog(d, -1); 569 return EINPROGRESS; 570 } 571 SLIST_FOREACH(sce, &d->channels, link) { 572 c = sce->channel; 573 CHN_LOCK(c); 574 if (c->direction == PCMDIR_PLAY) { 575 if (c->flags & CHN_F_VIRTUAL) { 576 /* Sanity check */ 577 if (ch != NULL && ch != c->parentchannel) { 578 CHN_UNLOCK(c); 579 pcm_inprog(d, -1); 580 return EINVAL; 581 } 582 if (req->newptr != NULL && 583 (c->flags & CHN_F_BUSY)) { 584 CHN_UNLOCK(c); 585 pcm_inprog(d, -1); 586 return EBUSY; 587 } 588 } else if (c->flags & CHN_F_HAS_VCHAN) { 589 /* No way!! */ 590 if (ch != NULL) { 591 CHN_UNLOCK(c); 592 pcm_inprog(d, -1); 593 return EINVAL; 594 } 595 ch = c; 596 if (ch->format != afmt2afmtstr(vchan_supported_fmts, 597 ch->format, fmtstr, sizeof(fmtstr), 598 AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) { 599 strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, sizeof(fmtstr)); 600 } 601 } 602 } 603 CHN_UNLOCK(c); 604 } 605 if (ch == NULL) { 606 pcm_inprog(d, -1); 607 return EINVAL; 608 } 609 err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 610 if (err == 0 && req->newptr != NULL) { 611 for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) { 612 if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) { 613 strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, sizeof(fmtstr)); 614 break; 615 } 616 } 617 newfmt = vchan_valid_strformat(fmtstr); 618 if (newfmt == 0) { 619 pcm_inprog(d, -1); 620 return EINVAL; 621 } 622 CHN_LOCK(ch); 623 if (newfmt != ch->format) { 624 /* Get channel speed, before chn_reset() screw it. */ 625 spd = ch->speed; 626 err = chn_reset(ch, newfmt); 627 if (err == 0) 628 err = chn_setspeed(ch, spd); 629 CHN_UNLOCK(ch); 630 if (err == 0) { 631 fake = pcm_getfakechan(d); 632 if (fake != NULL) { 633 CHN_LOCK(fake); 634 fake->format = newfmt; 635 CHN_UNLOCK(fake); 636 } 637 } 638 } else 639 CHN_UNLOCK(ch); 640 } 641 pcm_inprog(d, -1); 642 return err; 643 } 644 #endif 645 646 /* virtual channel interface */ 647 648 int 649 vchan_create(struct pcm_channel *parent) 650 { 651 struct snddev_info *d = parent->parentsnddev; 652 struct pcmchan_children *pce; 653 struct pcm_channel *child, *fake; 654 struct pcmchan_caps *parent_caps; 655 uint32_t vchanfmt = 0; 656 int err, first, speed = 0, r; 657 658 if (!(parent->flags & CHN_F_BUSY)) 659 return EBUSY; 660 661 662 CHN_UNLOCK(parent); 663 664 pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 665 666 /* create a new playback channel */ 667 child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 668 if (!child) { 669 free(pce, M_DEVBUF); 670 CHN_LOCK(parent); 671 return ENODEV; 672 } 673 pce->channel = child; 674 675 /* add us to our grandparent's channel list */ 676 /* 677 * XXX maybe we shouldn't always add the dev_t 678 */ 679 err = pcm_chn_add(d, child); 680 if (err) { 681 pcm_chn_destroy(child); 682 free(pce, M_DEVBUF); 683 CHN_LOCK(parent); 684 return err; 685 } 686 687 CHN_LOCK(parent); 688 /* add us to our parent channel's children */ 689 first = SLIST_EMPTY(&parent->children); 690 SLIST_INSERT_HEAD(&parent->children, pce, link); 691 parent->flags |= CHN_F_HAS_VCHAN; 692 693 if (first) { 694 parent_caps = chn_getcaps(parent); 695 if (parent_caps == NULL) 696 err = EINVAL; 697 698 fake = pcm_getfakechan(d); 699 700 if (!err && fake != NULL) { 701 /* 702 * Avoid querying kernel hint, use saved value 703 * from fake channel. 704 */ 705 CHN_UNLOCK(parent); 706 CHN_LOCK(fake); 707 speed = fake->speed; 708 vchanfmt = fake->format; 709 CHN_UNLOCK(fake); 710 CHN_LOCK(parent); 711 } 712 713 if (!err) { 714 if (vchanfmt == 0) { 715 const char *vfmt; 716 717 CHN_UNLOCK(parent); 718 r = resource_string_value(device_get_name(parent->dev), 719 device_get_unit(parent->dev), 720 "vchanformat", &vfmt); 721 CHN_LOCK(parent); 722 if (r != 0) 723 vfmt = NULL; 724 if (vfmt != NULL) { 725 vchanfmt = vchan_valid_strformat(vfmt); 726 for (r = 0; vchanfmt == 0 && 727 vchan_fmtstralias[r].alias != NULL; 728 r++) { 729 if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) { 730 vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr); 731 break; 732 } 733 } 734 } 735 if (vchanfmt == 0) 736 vchanfmt = VCHAN_DEFAULT_AFMT; 737 } 738 err = chn_reset(parent, vchanfmt); 739 } 740 741 if (!err) { 742 /* 743 * This is very sad. Few soundcards advertised as being 744 * able to do (insanely) higher/lower speed, but in 745 * reality, they simply can't. At least, we give user chance 746 * to set sane value via kernel hints or sysctl. 747 */ 748 if (speed < 1) { 749 CHN_UNLOCK(parent); 750 r = resource_int_value(device_get_name(parent->dev), 751 device_get_unit(parent->dev), 752 "vchanrate", &speed); 753 CHN_LOCK(parent); 754 if (r != 0) { 755 /* 756 * No saved value from fake channel, 757 * no hint, NOTHING. 758 * 759 * Workaround for sb16 running 760 * poorly at 45k / 49k. 761 */ 762 switch (parent_caps->maxspeed) { 763 case 45000: 764 case 49000: 765 speed = 44100; 766 break; 767 default: 768 speed = VCHAN_DEFAULT_SPEED; 769 if (speed > parent_caps->maxspeed) 770 speed = parent_caps->maxspeed; 771 break; 772 } 773 if (speed < parent_caps->minspeed) 774 speed = parent_caps->minspeed; 775 } 776 } 777 778 if (feeder_rate_round) { 779 /* 780 * Limit speed based on driver caps. 781 * This is supposed to help fixed rate, non-VRA 782 * AC97 cards, but.. (see below) 783 */ 784 if (speed < parent_caps->minspeed) 785 speed = parent_caps->minspeed; 786 if (speed > parent_caps->maxspeed) 787 speed = parent_caps->maxspeed; 788 } 789 790 /* 791 * We still need to limit the speed between 792 * feeder_rate_min <-> feeder_rate_max. This is 793 * just an escape goat if all of the above failed 794 * miserably. 795 */ 796 if (speed < feeder_rate_min) 797 speed = feeder_rate_min; 798 if (speed > feeder_rate_max) 799 speed = feeder_rate_max; 800 801 err = chn_setspeed(parent, speed); 802 /* 803 * Try to avoid FEEDER_RATE on parent channel if the 804 * requested value is not supported by the hardware. 805 */ 806 if (!err && feeder_rate_round && 807 (parent->feederflags & (1 << FEEDER_RATE))) { 808 speed = sndbuf_getspd(parent->bufhard); 809 err = chn_setspeed(parent, speed); 810 } 811 812 if (!err && fake != NULL) { 813 /* 814 * Save new value to fake channel. 815 */ 816 CHN_UNLOCK(parent); 817 CHN_LOCK(fake); 818 fake->speed = speed; 819 fake->format = vchanfmt; 820 CHN_UNLOCK(fake); 821 CHN_LOCK(parent); 822 } 823 } 824 825 if (err) { 826 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 827 parent->flags &= ~CHN_F_HAS_VCHAN; 828 CHN_UNLOCK(parent); 829 free(pce, M_DEVBUF); 830 if (pcm_chn_remove(d, child) == 0) 831 pcm_chn_destroy(child); 832 CHN_LOCK(parent); 833 return err; 834 } 835 } 836 837 return 0; 838 } 839 840 int 841 vchan_destroy(struct pcm_channel *c) 842 { 843 struct pcm_channel *parent = c->parentchannel; 844 struct snddev_info *d = parent->parentsnddev; 845 struct pcmchan_children *pce; 846 struct snddev_channel *sce; 847 uint32_t spd; 848 int err; 849 850 CHN_LOCK(parent); 851 if (!(parent->flags & CHN_F_BUSY)) { 852 CHN_UNLOCK(parent); 853 return EBUSY; 854 } 855 if (SLIST_EMPTY(&parent->children)) { 856 CHN_UNLOCK(parent); 857 return EINVAL; 858 } 859 860 /* remove us from our parent's children list */ 861 SLIST_FOREACH(pce, &parent->children, link) { 862 if (pce->channel == c) 863 goto gotch; 864 } 865 CHN_UNLOCK(parent); 866 return EINVAL; 867 gotch: 868 SLIST_FOREACH(sce, &d->channels, link) { 869 if (sce->channel == c) { 870 if (sce->dsp_devt) { 871 destroy_dev(sce->dsp_devt); 872 sce->dsp_devt = NULL; 873 } 874 if (sce->dspW_devt) { 875 destroy_dev(sce->dspW_devt); 876 sce->dspW_devt = NULL; 877 } 878 if (sce->audio_devt) { 879 destroy_dev(sce->audio_devt); 880 sce->audio_devt = NULL; 881 } 882 if (sce->dspHW_devt) { 883 destroy_dev(sce->dspHW_devt); 884 sce->dspHW_devt = NULL; 885 } 886 d->devcount--; 887 break; 888 } 889 } 890 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 891 free(pce, M_DEVBUF); 892 893 if (SLIST_EMPTY(&parent->children)) { 894 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 895 spd = parent->speed; 896 if (chn_reset(parent, parent->format) == 0) 897 chn_setspeed(parent, spd); 898 } 899 900 /* remove us from our grandparent's channel list */ 901 err = pcm_chn_remove(d, c); 902 903 CHN_UNLOCK(parent); 904 /* destroy ourselves */ 905 if (!err) 906 err = pcm_chn_destroy(c); 907 908 return err; 909 } 910 911 int 912 vchan_initsys(device_t dev) 913 { 914 #ifdef SND_DYNSYSCTL 915 struct snddev_info *d; 916 917 d = device_get_softc(dev); 918 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 919 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 920 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 921 sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); 922 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 923 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 924 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 925 sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate"); 926 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 927 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 928 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d), 929 sysctl_hw_snd_vchanformat, "A", "virtual channel format"); 930 #endif 931 932 return 0; 933 } 934