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 free(data, M_DEVBUF); 338 return 0; 339 } 340 341 static int 342 vchan_setformat(kobj_t obj, void *data, uint32_t format) 343 { 344 struct vchinfo *ch = data; 345 struct pcm_channel *parent = ch->parent; 346 struct pcm_channel *channel = ch->channel; 347 348 ch->fmt = format; 349 ch->bps = 1; 350 ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; 351 if (ch->fmt & AFMT_16BIT) 352 ch->bps <<= 1; 353 else if (ch->fmt & AFMT_24BIT) 354 ch->bps *= 3; 355 else if (ch->fmt & AFMT_32BIT) 356 ch->bps <<= 2; 357 CHN_UNLOCK(channel); 358 chn_notify(parent, CHN_N_FORMAT); 359 CHN_LOCK(channel); 360 sndbuf_setfmt(channel->bufsoft, format); 361 return 0; 362 } 363 364 static int 365 vchan_setspeed(kobj_t obj, void *data, uint32_t speed) 366 { 367 struct vchinfo *ch = data; 368 struct pcm_channel *parent = ch->parent; 369 struct pcm_channel *channel = ch->channel; 370 371 ch->spd = speed; 372 CHN_UNLOCK(channel); 373 CHN_LOCK(parent); 374 speed = sndbuf_getspd(parent->bufsoft); 375 CHN_UNLOCK(parent); 376 CHN_LOCK(channel); 377 return speed; 378 } 379 380 static int 381 vchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) 382 { 383 struct vchinfo *ch = data; 384 struct pcm_channel *channel = ch->channel; 385 struct pcm_channel *parent = ch->parent; 386 /* struct pcm_channel *channel = ch->channel; */ 387 int prate, crate; 388 389 ch->blksz = blocksize; 390 /* CHN_UNLOCK(channel); */ 391 sndbuf_setblksz(channel->bufhard, blocksize); 392 chn_notify(parent, CHN_N_BLOCKSIZE); 393 CHN_LOCK(parent); 394 /* CHN_LOCK(channel); */ 395 396 crate = ch->spd * ch->bps; 397 prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft); 398 blocksize = sndbuf_getblksz(parent->bufsoft); 399 CHN_UNLOCK(parent); 400 blocksize *= prate; 401 blocksize /= crate; 402 blocksize += ch->bps; 403 prate = 0; 404 while (blocksize >> prate) 405 prate++; 406 blocksize = 1 << (prate - 1); 407 blocksize -= blocksize % ch->bps; 408 /* XXX screwed !@#$ */ 409 if (blocksize < ch->bps) 410 blocksize = 4096 - (4096 % ch->bps); 411 412 return blocksize; 413 } 414 415 static int 416 vchan_trigger(kobj_t obj, void *data, int go) 417 { 418 struct vchinfo *ch = data; 419 struct pcm_channel *parent = ch->parent; 420 struct pcm_channel *channel = ch->channel; 421 422 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 423 return 0; 424 425 ch->run = (go == PCMTRIG_START)? 1 : 0; 426 CHN_UNLOCK(channel); 427 chn_notify(parent, CHN_N_TRIGGER); 428 CHN_LOCK(channel); 429 430 return 0; 431 } 432 433 static struct pcmchan_caps * 434 vchan_getcaps(kobj_t obj, void *data) 435 { 436 struct vchinfo *ch = data; 437 uint32_t fmt; 438 439 ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft); 440 ch->caps.maxspeed = ch->caps.minspeed; 441 ch->caps.caps = 0; 442 ch->fmts[1] = 0; 443 fmt = sndbuf_getfmt(ch->parent->bufsoft); 444 if (fmt != vchan_valid_format(fmt)) { 445 device_printf(ch->parent->dev, 446 "%s: WARNING: invalid vchan format! (0x%08x)\n", 447 __func__, fmt); 448 fmt = VCHAN_DEFAULT_AFMT; 449 } 450 ch->fmts[0] = fmt; 451 ch->caps.fmtlist = ch->fmts; 452 453 return &ch->caps; 454 } 455 456 static kobj_method_t vchan_methods[] = { 457 KOBJMETHOD(channel_init, vchan_init), 458 KOBJMETHOD(channel_free, vchan_free), 459 KOBJMETHOD(channel_setformat, vchan_setformat), 460 KOBJMETHOD(channel_setspeed, vchan_setspeed), 461 KOBJMETHOD(channel_setblocksize, vchan_setblocksize), 462 KOBJMETHOD(channel_trigger, vchan_trigger), 463 KOBJMETHOD(channel_getcaps, vchan_getcaps), 464 {0, 0} 465 }; 466 CHANNEL_DECLARE(vchan); 467 468 /* 469 * On the fly vchan rate settings 470 */ 471 #ifdef SND_DYNSYSCTL 472 static int 473 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) 474 { 475 struct snddev_info *d; 476 struct snddev_channel *sce; 477 struct pcm_channel *c, *ch = NULL, *fake; 478 struct pcmchan_caps *caps; 479 int err = 0; 480 int newspd = 0; 481 482 d = oidp->oid_arg1; 483 if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) 484 return EINVAL; 485 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 486 pcm_inprog(d, -1); 487 return EINPROGRESS; 488 } 489 SLIST_FOREACH(sce, &d->channels, link) { 490 c = sce->channel; 491 CHN_LOCK(c); 492 if (c->direction == PCMDIR_PLAY) { 493 if (c->flags & CHN_F_VIRTUAL) { 494 /* Sanity check */ 495 if (ch != NULL && ch != c->parentchannel) { 496 CHN_UNLOCK(c); 497 pcm_inprog(d, -1); 498 return EINVAL; 499 } 500 if (req->newptr != NULL && 501 (c->flags & CHN_F_BUSY)) { 502 CHN_UNLOCK(c); 503 pcm_inprog(d, -1); 504 return EBUSY; 505 } 506 } else if (c->flags & CHN_F_HAS_VCHAN) { 507 /* No way!! */ 508 if (ch != NULL) { 509 CHN_UNLOCK(c); 510 pcm_inprog(d, -1); 511 return EINVAL; 512 } 513 ch = c; 514 newspd = ch->speed; 515 } 516 } 517 CHN_UNLOCK(c); 518 } 519 if (ch == NULL) { 520 pcm_inprog(d, -1); 521 return EINVAL; 522 } 523 err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req); 524 if (err == 0 && req->newptr != NULL) { 525 if (newspd < 1 || newspd < feeder_rate_min || 526 newspd > feeder_rate_max) { 527 pcm_inprog(d, -1); 528 return EINVAL; 529 } 530 CHN_LOCK(ch); 531 if (feeder_rate_round) { 532 caps = chn_getcaps(ch); 533 if (caps == NULL || newspd < caps->minspeed || 534 newspd > caps->maxspeed) { 535 CHN_UNLOCK(ch); 536 pcm_inprog(d, -1); 537 return EINVAL; 538 } 539 } 540 if (newspd != ch->speed) { 541 err = chn_setspeed(ch, newspd); 542 /* 543 * Try to avoid FEEDER_RATE on parent channel if the 544 * requested value is not supported by the hardware. 545 */ 546 if (!err && feeder_rate_round && 547 (ch->feederflags & (1 << FEEDER_RATE))) { 548 newspd = sndbuf_getspd(ch->bufhard); 549 err = chn_setspeed(ch, newspd); 550 } 551 CHN_UNLOCK(ch); 552 if (err == 0) { 553 fake = pcm_getfakechan(d); 554 if (fake != NULL) { 555 CHN_LOCK(fake); 556 fake->speed = newspd; 557 CHN_UNLOCK(fake); 558 } 559 } 560 } else 561 CHN_UNLOCK(ch); 562 } 563 pcm_inprog(d, -1); 564 return err; 565 } 566 567 static int 568 sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) 569 { 570 struct snddev_info *d; 571 struct snddev_channel *sce; 572 struct pcm_channel *c, *ch = NULL, *fake; 573 uint32_t newfmt, spd; 574 char fmtstr[AFMTSTR_MAXSZ]; 575 int err = 0, i; 576 577 d = oidp->oid_arg1; 578 if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) 579 return EINVAL; 580 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 581 pcm_inprog(d, -1); 582 return EINPROGRESS; 583 } 584 SLIST_FOREACH(sce, &d->channels, link) { 585 c = sce->channel; 586 CHN_LOCK(c); 587 if (c->direction == PCMDIR_PLAY) { 588 if (c->flags & CHN_F_VIRTUAL) { 589 /* Sanity check */ 590 if (ch != NULL && ch != c->parentchannel) { 591 CHN_UNLOCK(c); 592 pcm_inprog(d, -1); 593 return EINVAL; 594 } 595 if (req->newptr != NULL && 596 (c->flags & CHN_F_BUSY)) { 597 CHN_UNLOCK(c); 598 pcm_inprog(d, -1); 599 return EBUSY; 600 } 601 } else if (c->flags & CHN_F_HAS_VCHAN) { 602 /* No way!! */ 603 if (ch != NULL) { 604 CHN_UNLOCK(c); 605 pcm_inprog(d, -1); 606 return EINVAL; 607 } 608 ch = c; 609 if (ch->format != afmt2afmtstr(vchan_supported_fmts, 610 ch->format, fmtstr, sizeof(fmtstr), 611 AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) { 612 strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, sizeof(fmtstr)); 613 } 614 } 615 } 616 CHN_UNLOCK(c); 617 } 618 if (ch == NULL) { 619 pcm_inprog(d, -1); 620 return EINVAL; 621 } 622 err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 623 if (err == 0 && req->newptr != NULL) { 624 for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) { 625 if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) { 626 strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, sizeof(fmtstr)); 627 break; 628 } 629 } 630 newfmt = vchan_valid_strformat(fmtstr); 631 if (newfmt == 0) { 632 pcm_inprog(d, -1); 633 return EINVAL; 634 } 635 CHN_LOCK(ch); 636 if (newfmt != ch->format) { 637 /* Get channel speed, before chn_reset() screw it. */ 638 spd = ch->speed; 639 err = chn_reset(ch, newfmt); 640 if (err == 0) 641 err = chn_setspeed(ch, spd); 642 CHN_UNLOCK(ch); 643 if (err == 0) { 644 fake = pcm_getfakechan(d); 645 if (fake != NULL) { 646 CHN_LOCK(fake); 647 fake->format = newfmt; 648 CHN_UNLOCK(fake); 649 } 650 } 651 } else 652 CHN_UNLOCK(ch); 653 } 654 pcm_inprog(d, -1); 655 return err; 656 } 657 #endif 658 659 /* virtual channel interface */ 660 661 int 662 vchan_create(struct pcm_channel *parent) 663 { 664 struct snddev_info *d = parent->parentsnddev; 665 struct pcmchan_children *pce; 666 struct pcm_channel *child, *fake; 667 struct pcmchan_caps *parent_caps; 668 uint32_t vchanfmt = 0; 669 int err, first, speed = 0, r; 670 671 if (!(parent->flags & CHN_F_BUSY)) 672 return EBUSY; 673 674 675 CHN_UNLOCK(parent); 676 677 pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 678 if (!pce) { 679 CHN_LOCK(parent); 680 return ENOMEM; 681 } 682 683 /* create a new playback channel */ 684 child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 685 if (!child) { 686 free(pce, M_DEVBUF); 687 CHN_LOCK(parent); 688 return ENODEV; 689 } 690 pce->channel = child; 691 692 /* add us to our grandparent's channel list */ 693 /* 694 * XXX maybe we shouldn't always add the dev_t 695 */ 696 err = pcm_chn_add(d, child); 697 if (err) { 698 pcm_chn_destroy(child); 699 free(pce, M_DEVBUF); 700 CHN_LOCK(parent); 701 return err; 702 } 703 704 CHN_LOCK(parent); 705 /* add us to our parent channel's children */ 706 first = SLIST_EMPTY(&parent->children); 707 SLIST_INSERT_HEAD(&parent->children, pce, link); 708 parent->flags |= CHN_F_HAS_VCHAN; 709 710 if (first) { 711 parent_caps = chn_getcaps(parent); 712 if (parent_caps == NULL) 713 err = EINVAL; 714 715 fake = pcm_getfakechan(d); 716 717 if (!err && fake != NULL) { 718 /* 719 * Avoid querying kernel hint, use saved value 720 * from fake channel. 721 */ 722 CHN_UNLOCK(parent); 723 CHN_LOCK(fake); 724 speed = fake->speed; 725 vchanfmt = fake->format; 726 CHN_UNLOCK(fake); 727 CHN_LOCK(parent); 728 } 729 730 if (!err) { 731 if (vchanfmt == 0) { 732 const char *vfmt; 733 734 CHN_UNLOCK(parent); 735 r = resource_string_value(device_get_name(parent->dev), 736 device_get_unit(parent->dev), 737 "vchanformat", &vfmt); 738 CHN_LOCK(parent); 739 if (r != 0) 740 vfmt = NULL; 741 if (vfmt != NULL) { 742 vchanfmt = vchan_valid_strformat(vfmt); 743 for (r = 0; vchanfmt == 0 && 744 vchan_fmtstralias[r].alias != NULL; 745 r++) { 746 if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) { 747 vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr); 748 break; 749 } 750 } 751 } 752 if (vchanfmt == 0) 753 vchanfmt = VCHAN_DEFAULT_AFMT; 754 } 755 err = chn_reset(parent, vchanfmt); 756 } 757 758 if (!err) { 759 /* 760 * This is very sad. Few soundcards advertised as being 761 * able to do (insanely) higher/lower speed, but in 762 * reality, they simply can't. At least, we give user chance 763 * to set sane value via kernel hints or sysctl. 764 */ 765 if (speed < 1) { 766 CHN_UNLOCK(parent); 767 r = resource_int_value(device_get_name(parent->dev), 768 device_get_unit(parent->dev), 769 "vchanrate", &speed); 770 CHN_LOCK(parent); 771 if (r != 0) { 772 /* 773 * No saved value from fake channel, 774 * no hint, NOTHING. 775 * 776 * Workaround for sb16 running 777 * poorly at 45k / 49k. 778 */ 779 switch (parent_caps->maxspeed) { 780 case 45000: 781 case 49000: 782 speed = 44100; 783 break; 784 default: 785 speed = VCHAN_DEFAULT_SPEED; 786 if (speed > parent_caps->maxspeed) 787 speed = parent_caps->maxspeed; 788 break; 789 } 790 if (speed < parent_caps->minspeed) 791 speed = parent_caps->minspeed; 792 } 793 } 794 795 if (feeder_rate_round) { 796 /* 797 * Limit speed based on driver caps. 798 * This is supposed to help fixed rate, non-VRA 799 * AC97 cards, but.. (see below) 800 */ 801 if (speed < parent_caps->minspeed) 802 speed = parent_caps->minspeed; 803 if (speed > parent_caps->maxspeed) 804 speed = parent_caps->maxspeed; 805 } 806 807 /* 808 * We still need to limit the speed between 809 * feeder_rate_min <-> feeder_rate_max. This is 810 * just an escape goat if all of the above failed 811 * miserably. 812 */ 813 if (speed < feeder_rate_min) 814 speed = feeder_rate_min; 815 if (speed > feeder_rate_max) 816 speed = feeder_rate_max; 817 818 err = chn_setspeed(parent, speed); 819 /* 820 * Try to avoid FEEDER_RATE on parent channel if the 821 * requested value is not supported by the hardware. 822 */ 823 if (!err && feeder_rate_round && 824 (parent->feederflags & (1 << FEEDER_RATE))) { 825 speed = sndbuf_getspd(parent->bufhard); 826 err = chn_setspeed(parent, speed); 827 } 828 829 if (!err && fake != NULL) { 830 /* 831 * Save new value to fake channel. 832 */ 833 CHN_UNLOCK(parent); 834 CHN_LOCK(fake); 835 fake->speed = speed; 836 fake->format = vchanfmt; 837 CHN_UNLOCK(fake); 838 CHN_LOCK(parent); 839 } 840 } 841 842 if (err) { 843 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 844 parent->flags &= ~CHN_F_HAS_VCHAN; 845 CHN_UNLOCK(parent); 846 free(pce, M_DEVBUF); 847 if (pcm_chn_remove(d, child) == 0) 848 pcm_chn_destroy(child); 849 CHN_LOCK(parent); 850 return err; 851 } 852 } 853 854 return 0; 855 } 856 857 int 858 vchan_destroy(struct pcm_channel *c) 859 { 860 struct pcm_channel *parent = c->parentchannel; 861 struct snddev_info *d = parent->parentsnddev; 862 struct pcmchan_children *pce; 863 struct snddev_channel *sce; 864 uint32_t spd; 865 int err; 866 867 CHN_LOCK(parent); 868 if (!(parent->flags & CHN_F_BUSY)) { 869 CHN_UNLOCK(parent); 870 return EBUSY; 871 } 872 if (SLIST_EMPTY(&parent->children)) { 873 CHN_UNLOCK(parent); 874 return EINVAL; 875 } 876 877 /* remove us from our parent's children list */ 878 SLIST_FOREACH(pce, &parent->children, link) { 879 if (pce->channel == c) 880 goto gotch; 881 } 882 CHN_UNLOCK(parent); 883 return EINVAL; 884 gotch: 885 SLIST_FOREACH(sce, &d->channels, link) { 886 if (sce->channel == c) { 887 if (sce->dsp_devt) { 888 destroy_dev(sce->dsp_devt); 889 sce->dsp_devt = NULL; 890 } 891 if (sce->dspW_devt) { 892 destroy_dev(sce->dspW_devt); 893 sce->dspW_devt = NULL; 894 } 895 if (sce->audio_devt) { 896 destroy_dev(sce->audio_devt); 897 sce->audio_devt = NULL; 898 } 899 if (sce->dspHW_devt) { 900 destroy_dev(sce->dspHW_devt); 901 sce->dspHW_devt = NULL; 902 } 903 d->devcount--; 904 break; 905 } 906 } 907 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 908 free(pce, M_DEVBUF); 909 910 if (SLIST_EMPTY(&parent->children)) { 911 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 912 spd = parent->speed; 913 if (chn_reset(parent, parent->format) == 0) 914 chn_setspeed(parent, spd); 915 } 916 917 /* remove us from our grandparent's channel list */ 918 err = pcm_chn_remove(d, c); 919 920 CHN_UNLOCK(parent); 921 /* destroy ourselves */ 922 if (!err) 923 err = pcm_chn_destroy(c); 924 925 return err; 926 } 927 928 int 929 vchan_initsys(device_t dev) 930 { 931 #ifdef SND_DYNSYSCTL 932 struct snddev_info *d; 933 934 d = device_get_softc(dev); 935 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 936 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 937 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 938 sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); 939 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 940 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 941 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 942 sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate"); 943 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 944 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 945 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d), 946 sysctl_hw_snd_vchanformat, "A", "virtual channel format"); 947 #endif 948 949 return 0; 950 } 951