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