vchan.c revision 170521
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 34SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/vchan.c 170521 2007-06-11 00:49:46Z ariff $"); 35 36MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder"); 37 38typedef uint32_t (*feed_vchan_mixer)(uint8_t *, uint8_t *, uint32_t); 39 40struct 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 */ 48static 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. */ 60static 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) \ 94static uint32_t \ 95feed_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 120FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne) 121FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le) 122FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le) 123FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le) 124FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be) 125FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be) 126FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be) 127FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne) 128FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le) 129FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le) 130FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le) 131FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be) 132FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be) 133FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be) 134 135struct feed_vchan_info { 136 uint32_t format; 137 int bps; 138 feed_vchan_mixer mix; 139}; 140 141static 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 162static int 163feed_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 184static __inline int 185feed_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 bs = ch->bufsoft; 226 cnt = sndbuf_getfree(bs); 227 if (!(ch->flags & CHN_F_TRIGGERED) || 228 cnt < sndbuf_getbps(bs)) { 229 CHN_UNLOCK(ch); 230 continue; 231 } 232 do { 233 cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, cnt, b); 234 if (cnt != 0) { 235 sndbuf_acquire(bs, bs->tmpbuf, cnt); 236 cnt = sndbuf_getfree(bs); 237 } 238 } while (cnt != 0); 239 /* 240 * Not entirely flushed out... 241 */ 242 if (b->rl != 0) 243 ch->xruns++; 244 CHN_UNLOCK(ch); 245 /* 246 * Rewind buffer position for next virtual channel. 247 */ 248 b->rp = 0; 249 b->rl = rdy; 250 } 251 252 /* 253 * Set ready pointer to indicate that our children are ready 254 * to be woken up, also as an interrupt threshold progress 255 * indicator. 256 */ 257 b->rl = 1; 258 259 /* 260 * Return 0 to bail out early from sndbuf_feed() loop. 261 * No need to increase feedcount counter since part of this 262 * feeder chains already include feed_root(). 263 */ 264 return (0); 265} 266 267static int 268feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 269 uint32_t count, void *source) 270{ 271 struct feed_vchan_info *info; 272 struct snd_dbuf *src = source; 273 struct pcm_channel *ch; 274 uint32_t cnt, mcnt, rcnt, sz; 275 uint8_t *tmp; 276 277 if (c->direction == PCMDIR_REC) 278 return (feed_vchan_rec(c)); 279 280 sz = sndbuf_getsize(src); 281 if (sz < count) 282 count = sz; 283 284 info = &feed_vchan_info_tbl[FVCHAN_INFOIDX((intptr_t)f->data)]; 285 sz = info->bps * FVCHAN_CHANNELS((intptr_t)f->data); 286 count -= count % sz; 287 if (count < sz) 288 return (0); 289 290 /* 291 * we are going to use our source as a temporary buffer since it's 292 * got no other purpose. we obtain our data by traversing the channel 293 * list of children and calling vchan_mix_* to mix count bytes from 294 * each into our destination buffer, b 295 */ 296 tmp = sndbuf_getbuf(src); 297 rcnt = 0; 298 mcnt = 0; 299 300 CHN_FOREACH(ch, c, children.busy) { 301 CHN_LOCK(ch); 302 if (!(ch->flags & CHN_F_TRIGGERED)) { 303 CHN_UNLOCK(ch); 304 continue; 305 } 306 if ((ch->flags & CHN_F_MAPPED) && !(ch->flags & CHN_F_CLOSING)) 307 sndbuf_acquire(ch->bufsoft, NULL, 308 sndbuf_getfree(ch->bufsoft)); 309 if (rcnt == 0) { 310 rcnt = FEEDER_FEED(ch->feeder, ch, b, count, 311 ch->bufsoft); 312 rcnt -= rcnt % sz; 313 mcnt = count - rcnt; 314 } else { 315 cnt = FEEDER_FEED(ch->feeder, ch, tmp, count, 316 ch->bufsoft); 317 cnt -= cnt % sz; 318 if (cnt != 0) { 319 if (mcnt != 0) { 320 memset(b + rcnt, 321 sndbuf_zerodata(f->desc->out), 322 mcnt); 323 mcnt = 0; 324 } 325 cnt = info->mix(b, tmp, cnt); 326 if (cnt > rcnt) 327 rcnt = cnt; 328 } 329 } 330 CHN_UNLOCK(ch); 331 } 332 333 if (++c->feedcount == 0) 334 c->feedcount = 2; 335 336 return (rcnt); 337} 338 339static struct pcm_feederdesc feeder_vchan_desc[] = { 340 {FEEDER_MIXER, AFMT_S8, AFMT_S8, 0}, 341 {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0}, 342 {FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0}, 343 {FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0}, 344 {FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0}, 345 {FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0}, 346 {FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0}, 347 {FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, 348 {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 349 {FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, 350 {FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, 351 {FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, 352 {FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, 353 {FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, 354 {FEEDER_MIXER, AFMT_U8, AFMT_U8, 0}, 355 {FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0}, 356 {FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0}, 357 {FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0}, 358 {FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0}, 359 {FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0}, 360 {FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0}, 361 {FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, 362 {FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, 363 {FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, 364 {FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, 365 {FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, 366 {FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, 367 {FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, 368 {0, 0, 0, 0}, 369}; 370static kobj_method_t feeder_vchan_methods[] = { 371 KOBJMETHOD(feeder_init, feed_vchan_init), 372 KOBJMETHOD(feeder_feed, feed_vchan), 373 {0, 0} 374}; 375FEEDER_DECLARE(feeder_vchan, 2, NULL); 376 377/************************************************************/ 378 379static void * 380vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 381 struct pcm_channel *c, int dir) 382{ 383 struct vchinfo *ch; 384 385 KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC, 386 ("vchan_init: bad direction")); 387 KASSERT(c != NULL && c->parentchannel != NULL, 388 ("vchan_init: bad channels")); 389 390 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 391 ch->channel = c; 392 ch->trigger = PCMTRIG_STOP; 393 394 c->flags |= CHN_F_VIRTUAL; 395 396 return (ch); 397} 398 399static int 400vchan_free(kobj_t obj, void *data) 401{ 402 free(data, M_DEVBUF); 403 404 return (0); 405} 406 407static int 408vchan_setformat(kobj_t obj, void *data, uint32_t format) 409{ 410 struct vchinfo *ch = data; 411 412 if (fmtvalid(format, ch->fmtlist) == 0) 413 return (-1); 414 415 return (0); 416} 417 418static int 419vchan_setspeed(kobj_t obj, void *data, uint32_t speed) 420{ 421 struct vchinfo *ch = data; 422 struct pcm_channel *p = ch->channel->parentchannel; 423 424 return (sndbuf_getspd(p->bufsoft)); 425} 426 427static int 428vchan_trigger(kobj_t obj, void *data, int go) 429{ 430 struct vchinfo *ch = data; 431 struct pcm_channel *c, *p; 432 int otrigger; 433 434 if (!PCMTRIG_COMMON(go) || go == ch->trigger) 435 return (0); 436 437 c = ch->channel; 438 p = c->parentchannel; 439 otrigger = ch->trigger; 440 ch->trigger = go; 441 442 CHN_UNLOCK(c); 443 CHN_LOCK(p); 444 445 switch (go) { 446 case PCMTRIG_START: 447 if (otrigger != PCMTRIG_START) { 448 CHN_INSERT_HEAD(p, c, children.busy); 449 } 450 break; 451 case PCMTRIG_STOP: 452 case PCMTRIG_ABORT: 453 if (otrigger == PCMTRIG_START) { 454 CHN_REMOVE(p, c, children.busy); 455 } 456 break; 457 default: 458 break; 459 } 460 461 CHN_UNLOCK(p); 462 chn_notify(p, CHN_N_TRIGGER); 463 CHN_LOCK(c); 464 465 return (0); 466} 467 468static struct pcmchan_caps * 469vchan_getcaps(kobj_t obj, void *data) 470{ 471 struct vchinfo *ch = data; 472 struct pcm_channel *c, *p; 473 uint32_t fmt; 474 475 c = ch->channel; 476 p = c->parentchannel; 477 ch->caps.minspeed = sndbuf_getspd(p->bufsoft); 478 ch->caps.maxspeed = ch->caps.minspeed; 479 ch->caps.caps = 0; 480 ch->fmtlist[1] = 0; 481 fmt = sndbuf_getfmt(p->bufsoft); 482 if (fmt != vchan_valid_format(fmt)) { 483 device_printf(c->dev, 484 "%s: WARNING: invalid vchan format! (0x%08x)\n", 485 __func__, fmt); 486 fmt = VCHAN_DEFAULT_AFMT; 487 } 488 ch->fmtlist[0] = fmt; 489 ch->caps.fmtlist = ch->fmtlist; 490 491 return (&ch->caps); 492} 493 494static kobj_method_t vchan_methods[] = { 495 KOBJMETHOD(channel_init, vchan_init), 496 KOBJMETHOD(channel_free, vchan_free), 497 KOBJMETHOD(channel_setformat, vchan_setformat), 498 KOBJMETHOD(channel_setspeed, vchan_setspeed), 499 KOBJMETHOD(channel_trigger, vchan_trigger), 500 KOBJMETHOD(channel_getcaps, vchan_getcaps), 501 {0, 0} 502}; 503CHANNEL_DECLARE(vchan); 504 505/* 506 * On the fly vchan rate settings 507 */ 508#ifdef SND_DYNSYSCTL 509static int 510sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) 511{ 512 struct snddev_info *d; 513 struct pcm_channel *c, *ch = NULL; 514 struct pcmchan_caps *caps; 515 int vchancount, *vchanrate; 516 int direction; 517 int err = 0; 518 int newspd = 0; 519 520 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 521 if (d == NULL || !(d->flags & SD_F_AUTOVCHAN)) 522 return (EINVAL); 523 524 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 525 case VCHAN_PLAY: 526 direction = PCMDIR_PLAY; 527 vchancount = d->pvchancount; 528 vchanrate = &d->pvchanrate; 529 break; 530 case VCHAN_REC: 531 direction = PCMDIR_REC; 532 vchancount = d->rvchancount; 533 vchanrate = &d->rvchanrate; 534 break; 535 default: 536 return (EINVAL); 537 break; 538 } 539 540 if (vchancount < 1) 541 return (EINVAL); 542 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 543 pcm_inprog(d, -1); 544 return (EINPROGRESS); 545 } 546 CHN_FOREACH(c, d, channels.pcm) { 547 CHN_LOCK(c); 548 if (c->direction == direction) { 549 if (c->flags & CHN_F_VIRTUAL) { 550 /* Sanity check */ 551 if (ch != NULL && ch != c->parentchannel) { 552 CHN_UNLOCK(c); 553 pcm_inprog(d, -1); 554 return (EINVAL); 555 } 556 if (req->newptr != NULL && 557 (c->flags & CHN_F_BUSY)) { 558 CHN_UNLOCK(c); 559 pcm_inprog(d, -1); 560 return (EBUSY); 561 } 562 } else if (c->flags & CHN_F_HAS_VCHAN) { 563 /* No way!! */ 564 if (ch != NULL) { 565 CHN_UNLOCK(c); 566 pcm_inprog(d, -1); 567 return (EINVAL); 568 } 569 ch = c; 570 newspd = ch->speed; 571 } 572 } 573 CHN_UNLOCK(c); 574 } 575 if (ch == NULL) { 576 pcm_inprog(d, -1); 577 return (EINVAL); 578 } 579 err = sysctl_handle_int(oidp, &newspd, 0, req); 580 if (err == 0 && req->newptr != NULL) { 581 if (newspd < 1 || newspd < feeder_rate_min || 582 newspd > feeder_rate_max) { 583 pcm_inprog(d, -1); 584 return (EINVAL); 585 } 586 CHN_LOCK(ch); 587 if (feeder_rate_round) { 588 caps = chn_getcaps(ch); 589 if (caps == NULL || newspd < caps->minspeed || 590 newspd > caps->maxspeed) { 591 CHN_UNLOCK(ch); 592 pcm_inprog(d, -1); 593 return (EINVAL); 594 } 595 } 596 if (newspd != ch->speed) { 597 err = chn_setspeed(ch, newspd); 598 /* 599 * Try to avoid FEEDER_RATE on parent channel if the 600 * requested value is not supported by the hardware. 601 */ 602 if (!err && feeder_rate_round && 603 (ch->feederflags & (1 << FEEDER_RATE))) { 604 newspd = sndbuf_getspd(ch->bufhard); 605 err = chn_setspeed(ch, newspd); 606 } 607 CHN_UNLOCK(ch); 608 if (err == 0) { 609 pcm_lock(d); 610 *vchanrate = newspd; 611 pcm_unlock(d); 612 } 613 } else 614 CHN_UNLOCK(ch); 615 } 616 pcm_inprog(d, -1); 617 return (err); 618} 619 620static int 621sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) 622{ 623 struct snddev_info *d; 624 struct pcm_channel *c, *ch = NULL; 625 uint32_t newfmt, spd; 626 int vchancount, *vchanformat; 627 int direction; 628 int err = 0, i; 629 char fmtstr[AFMTSTR_MAXSZ]; 630 631 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 632 if (d == NULL || !(d->flags & SD_F_AUTOVCHAN)) 633 return (EINVAL); 634 635 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 636 case VCHAN_PLAY: 637 direction = PCMDIR_PLAY; 638 vchancount = d->pvchancount; 639 vchanformat = &d->pvchanformat; 640 break; 641 case VCHAN_REC: 642 direction = PCMDIR_REC; 643 vchancount = d->rvchancount; 644 vchanformat = &d->rvchanformat; 645 break; 646 default: 647 return (EINVAL); 648 break; 649 } 650 651 if (vchancount < 1) 652 return (EINVAL); 653 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 654 pcm_inprog(d, -1); 655 return (EINPROGRESS); 656 } 657 CHN_FOREACH(c, d, channels.pcm) { 658 CHN_LOCK(c); 659 if (c->direction == direction) { 660 if (c->flags & CHN_F_VIRTUAL) { 661 /* Sanity check */ 662 if (ch != NULL && ch != c->parentchannel) { 663 CHN_UNLOCK(c); 664 pcm_inprog(d, -1); 665 return (EINVAL); 666 } 667 if (req->newptr != NULL && 668 (c->flags & CHN_F_BUSY)) { 669 CHN_UNLOCK(c); 670 pcm_inprog(d, -1); 671 return (EBUSY); 672 } 673 } else if (c->flags & CHN_F_HAS_VCHAN) { 674 /* No way!! */ 675 if (ch != NULL) { 676 CHN_UNLOCK(c); 677 pcm_inprog(d, -1); 678 return (EINVAL); 679 } 680 ch = c; 681 if (ch->format != 682 afmt2afmtstr(vchan_supported_fmts, 683 ch->format, fmtstr, sizeof(fmtstr), 684 AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) { 685 strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, 686 sizeof(fmtstr)); 687 } 688 } 689 } 690 CHN_UNLOCK(c); 691 } 692 if (ch == NULL) { 693 pcm_inprog(d, -1); 694 return (EINVAL); 695 } 696 err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 697 if (err == 0 && req->newptr != NULL) { 698 for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) { 699 if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) { 700 strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, 701 sizeof(fmtstr)); 702 break; 703 } 704 } 705 newfmt = vchan_valid_strformat(fmtstr); 706 if (newfmt == 0) { 707 pcm_inprog(d, -1); 708 return (EINVAL); 709 } 710 CHN_LOCK(ch); 711 if (newfmt != ch->format) { 712 /* Get channel speed, before chn_reset() screw it. */ 713 spd = ch->speed; 714 err = chn_reset(ch, newfmt); 715 if (err == 0) 716 err = chn_setspeed(ch, spd); 717 CHN_UNLOCK(ch); 718 if (err == 0) { 719 pcm_lock(d); 720 *vchanformat = newfmt; 721 pcm_unlock(d); 722 } 723 } else 724 CHN_UNLOCK(ch); 725 } 726 pcm_inprog(d, -1); 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 738int 739vchan_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 if (!(parent->flags & CHN_F_BUSY)) 749 return (EBUSY); 750 751 if (parent->direction == PCMDIR_PLAY) { 752 direction = PCMDIR_PLAY_VIRTUAL; 753 vchanfmt = d->pvchanformat; 754 speed = d->pvchanrate; 755 } else if (parent->direction == PCMDIR_REC) { 756 direction = PCMDIR_REC_VIRTUAL; 757 vchanfmt = d->rvchanformat; 758 speed = d->rvchanrate; 759 } else 760 return (EINVAL); 761 CHN_UNLOCK(parent); 762 763 /* create a new playback channel */ 764 ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent); 765 if (ch == NULL) { 766 CHN_LOCK(parent); 767 return (ENODEV); 768 } 769 770 /* add us to our grandparent's channel list */ 771 err = pcm_chn_add(d, ch); 772 if (err) { 773 pcm_chn_destroy(ch); 774 CHN_LOCK(parent); 775 return (err); 776 } 777 778 CHN_LOCK(parent); 779 /* add us to our parent channel's children */ 780 first = CHN_EMPTY(parent, children); 781 after = NULL; 782 CHN_FOREACH(tmp, parent, children) { 783 if (CHN_CHAN(tmp) > CHN_CHAN(ch)) 784 after = tmp; 785 else if (CHN_CHAN(tmp) < CHN_CHAN(ch)) 786 break; 787 } 788 if (after != NULL) { 789 CHN_INSERT_AFTER(after, ch, children); 790 } else { 791 CHN_INSERT_HEAD(parent, ch, children); 792 } 793 parent->flags |= CHN_F_HAS_VCHAN; 794 795 if (first) { 796 parent_caps = chn_getcaps(parent); 797 if (parent_caps == NULL) 798 err = EINVAL; 799 800 if (!err) { 801 if (vchanfmt == 0) { 802 const char *vfmt; 803 804 CHN_UNLOCK(parent); 805 r = resource_string_value( 806 device_get_name(parent->dev), 807 device_get_unit(parent->dev), 808 VCHAN_FMT_HINT(direction), 809 &vfmt); 810 CHN_LOCK(parent); 811 if (r != 0) 812 vfmt = NULL; 813 if (vfmt != NULL) { 814 vchanfmt = vchan_valid_strformat(vfmt); 815 for (r = 0; vchanfmt == 0 && 816 vchan_fmtstralias[r].alias != NULL; 817 r++) { 818 if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) { 819 vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr); 820 break; 821 } 822 } 823 } 824 if (vchanfmt == 0) 825 vchanfmt = VCHAN_DEFAULT_AFMT; 826 } 827 err = chn_reset(parent, vchanfmt); 828 } 829 830 if (!err) { 831 /* 832 * This is very sad. Few soundcards advertised as being 833 * able to do (insanely) higher/lower speed, but in 834 * reality, they simply can't. At least, we give user chance 835 * to set sane value via kernel hints or sysctl. 836 */ 837 if (speed < 1) { 838 CHN_UNLOCK(parent); 839 r = resource_int_value( 840 device_get_name(parent->dev), 841 device_get_unit(parent->dev), 842 VCHAN_SPD_HINT(direction), 843 &speed); 844 CHN_LOCK(parent); 845 if (r != 0) { 846 /* 847 * No saved value, no hint, NOTHING. 848 * 849 * Workaround for sb16 running 850 * poorly at 45k / 49k. 851 */ 852 switch (parent_caps->maxspeed) { 853 case 45000: 854 case 49000: 855 speed = 44100; 856 break; 857 default: 858 speed = VCHAN_DEFAULT_SPEED; 859 if (speed > parent_caps->maxspeed) 860 speed = parent_caps->maxspeed; 861 break; 862 } 863 if (speed < parent_caps->minspeed) 864 speed = parent_caps->minspeed; 865 } 866 } 867 868 if (feeder_rate_round) { 869 /* 870 * Limit speed based on driver caps. 871 * This is supposed to help fixed rate, non-VRA 872 * AC97 cards, but.. (see below) 873 */ 874 if (speed < parent_caps->minspeed) 875 speed = parent_caps->minspeed; 876 if (speed > parent_caps->maxspeed) 877 speed = parent_caps->maxspeed; 878 } 879 880 /* 881 * We still need to limit the speed between 882 * feeder_rate_min <-> feeder_rate_max. This is 883 * just an escape goat if all of the above failed 884 * miserably. 885 */ 886 if (speed < feeder_rate_min) 887 speed = feeder_rate_min; 888 if (speed > feeder_rate_max) 889 speed = feeder_rate_max; 890 891 err = chn_setspeed(parent, speed); 892 /* 893 * Try to avoid FEEDER_RATE on parent channel if the 894 * requested value is not supported by the hardware. 895 */ 896 if (!err && feeder_rate_round && 897 (parent->feederflags & (1 << FEEDER_RATE))) { 898 speed = sndbuf_getspd(parent->bufhard); 899 err = chn_setspeed(parent, speed); 900 } 901 902 if (!err) { 903 /* 904 * Save new value. 905 */ 906 CHN_UNLOCK(parent); 907 pcm_lock(d); 908 if (direction == PCMDIR_PLAY_VIRTUAL) { 909 d->pvchanformat = vchanfmt; 910 d->pvchanrate = speed; 911 } else { 912 d->rvchanformat = vchanfmt; 913 d->rvchanrate = speed; 914 } 915 pcm_unlock(d); 916 CHN_LOCK(parent); 917 } 918 } 919 920 if (err) { 921 CHN_REMOVE(parent, ch, children); 922 parent->flags &= ~CHN_F_HAS_VCHAN; 923 CHN_UNLOCK(parent); 924 pcm_lock(d); 925 if (pcm_chn_remove(d, ch) == 0) { 926 pcm_unlock(d); 927 pcm_chn_destroy(ch); 928 } else 929 pcm_unlock(d); 930 CHN_LOCK(parent); 931 return (err); 932 } 933 } 934 935 return (0); 936} 937 938int 939vchan_destroy(struct pcm_channel *c) 940{ 941 struct pcm_channel *parent = c->parentchannel; 942 struct snddev_info *d = parent->parentsnddev; 943 uint32_t spd; 944 int err; 945 946 CHN_LOCK(parent); 947 if (!(parent->flags & CHN_F_BUSY)) { 948 CHN_UNLOCK(parent); 949 return (EBUSY); 950 } 951 if (CHN_EMPTY(parent, children)) { 952 CHN_UNLOCK(parent); 953 return (EINVAL); 954 } 955 956 /* remove us from our parent's children list */ 957 CHN_REMOVE(parent, c, children); 958 959 if (CHN_EMPTY(parent, children)) { 960 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 961 spd = parent->speed; 962 if (chn_reset(parent, parent->format) == 0) 963 chn_setspeed(parent, spd); 964 } 965 966 CHN_UNLOCK(parent); 967 968 /* remove us from our grandparent's channel list */ 969 pcm_lock(d); 970 err = pcm_chn_remove(d, c); 971 pcm_unlock(d); 972 973 /* destroy ourselves */ 974 if (!err) 975 err = pcm_chn_destroy(c); 976 977 return (err); 978} 979 980int 981vchan_initsys(device_t dev) 982{ 983#ifdef SND_DYNSYSCTL 984 struct snddev_info *d; 985 int unit; 986 987 unit = device_get_unit(dev); 988 d = device_get_softc(dev); 989 990 /* Play */ 991 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 992 SYSCTL_CHILDREN(d->play_sysctl_tree), 993 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, 994 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 995 sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); 996 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 997 SYSCTL_CHILDREN(d->play_sysctl_tree), 998 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, 999 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1000 sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate"); 1001 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1002 SYSCTL_CHILDREN(d->play_sysctl_tree), 1003 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, 1004 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1005 sysctl_hw_snd_vchanformat, "A", "virtual channel format"); 1006 /* Rec */ 1007 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1008 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1009 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, 1010 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1011 sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); 1012 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1013 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1014 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, 1015 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1016 sysctl_hw_snd_vchanrate, "I", "virtual channel base speed/rate"); 1017 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1018 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1019 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, 1020 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1021 sysctl_hw_snd_vchanformat, "A", "virtual channel format"); 1022#endif 1023 1024 return (0); 1025} 1026