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