vchan.c revision 170206
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 170206 2007-06-02 13:07:44Z joel $"); 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 (!(go == PCMTRIG_START || go == PCMTRIG_STOP || 435 go == PCMTRIG_ABORT) || go == ch->trigger) 436 return (0); 437 438 c = ch->channel; 439 p = c->parentchannel; 440 otrigger = ch->trigger; 441 ch->trigger = go; 442 443 CHN_UNLOCK(c); 444 CHN_LOCK(p); 445 446 switch (go) { 447 case PCMTRIG_START: 448 if (otrigger != PCMTRIG_START) { 449 CHN_INSERT_HEAD(p, c, children.busy); 450 } 451 break; 452 case PCMTRIG_STOP: 453 case PCMTRIG_ABORT: 454 if (otrigger == PCMTRIG_START) { 455 CHN_REMOVE(p, c, children.busy); 456 } 457 break; 458 default: 459 break; 460 } 461 462 CHN_UNLOCK(p); 463 chn_notify(p, CHN_N_TRIGGER); 464 CHN_LOCK(c); 465 466 return (0); 467} 468 469static struct pcmchan_caps * 470vchan_getcaps(kobj_t obj, void *data) 471{ 472 struct vchinfo *ch = data; 473 struct pcm_channel *c, *p; 474 uint32_t fmt; 475 476 c = ch->channel; 477 p = c->parentchannel; 478 ch->caps.minspeed = sndbuf_getspd(p->bufsoft); 479 ch->caps.maxspeed = ch->caps.minspeed; 480 ch->caps.caps = 0; 481 ch->fmtlist[1] = 0; 482 fmt = sndbuf_getfmt(p->bufsoft); 483 if (fmt != vchan_valid_format(fmt)) { 484 device_printf(c->dev, 485 "%s: WARNING: invalid vchan format! (0x%08x)\n", 486 __func__, fmt); 487 fmt = VCHAN_DEFAULT_AFMT; 488 } 489 ch->fmtlist[0] = fmt; 490 ch->caps.fmtlist = ch->fmtlist; 491 492 return (&ch->caps); 493} 494 495static kobj_method_t vchan_methods[] = { 496 KOBJMETHOD(channel_init, vchan_init), 497 KOBJMETHOD(channel_free, vchan_free), 498 KOBJMETHOD(channel_setformat, vchan_setformat), 499 KOBJMETHOD(channel_setspeed, vchan_setspeed), 500 KOBJMETHOD(channel_trigger, vchan_trigger), 501 KOBJMETHOD(channel_getcaps, vchan_getcaps), 502 {0, 0} 503}; 504CHANNEL_DECLARE(vchan); 505 506/* 507 * On the fly vchan rate settings 508 */ 509#ifdef SND_DYNSYSCTL 510static int 511sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) 512{ 513 struct snddev_info *d; 514 struct pcm_channel *c, *ch = NULL; 515 struct pcmchan_caps *caps; 516 int vchancount, *vchanrate; 517 int direction; 518 int err = 0; 519 int newspd = 0; 520 521 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 522 if (d == NULL || !(d->flags & SD_F_AUTOVCHAN)) 523 return (EINVAL); 524 525 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 526 case VCHAN_PLAY: 527 direction = PCMDIR_PLAY; 528 vchancount = d->pvchancount; 529 vchanrate = &d->pvchanrate; 530 break; 531 case VCHAN_REC: 532 direction = PCMDIR_REC; 533 vchancount = d->rvchancount; 534 vchanrate = &d->rvchanrate; 535 break; 536 default: 537 return (EINVAL); 538 break; 539 } 540 541 if (vchancount < 1) 542 return (EINVAL); 543 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 544 pcm_inprog(d, -1); 545 return (EINPROGRESS); 546 } 547 CHN_FOREACH(c, d, channels.pcm) { 548 CHN_LOCK(c); 549 if (c->direction == direction) { 550 if (c->flags & CHN_F_VIRTUAL) { 551 /* Sanity check */ 552 if (ch != NULL && ch != c->parentchannel) { 553 CHN_UNLOCK(c); 554 pcm_inprog(d, -1); 555 return (EINVAL); 556 } 557 if (req->newptr != NULL && 558 (c->flags & CHN_F_BUSY)) { 559 CHN_UNLOCK(c); 560 pcm_inprog(d, -1); 561 return (EBUSY); 562 } 563 } else if (c->flags & CHN_F_HAS_VCHAN) { 564 /* No way!! */ 565 if (ch != NULL) { 566 CHN_UNLOCK(c); 567 pcm_inprog(d, -1); 568 return (EINVAL); 569 } 570 ch = c; 571 newspd = ch->speed; 572 } 573 } 574 CHN_UNLOCK(c); 575 } 576 if (ch == NULL) { 577 pcm_inprog(d, -1); 578 return (EINVAL); 579 } 580 err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req); 581 if (err == 0 && req->newptr != NULL) { 582 if (newspd < 1 || newspd < feeder_rate_min || 583 newspd > feeder_rate_max) { 584 pcm_inprog(d, -1); 585 return (EINVAL); 586 } 587 CHN_LOCK(ch); 588 if (feeder_rate_round) { 589 caps = chn_getcaps(ch); 590 if (caps == NULL || newspd < caps->minspeed || 591 newspd > caps->maxspeed) { 592 CHN_UNLOCK(ch); 593 pcm_inprog(d, -1); 594 return (EINVAL); 595 } 596 } 597 if (newspd != ch->speed) { 598 err = chn_setspeed(ch, newspd); 599 /* 600 * Try to avoid FEEDER_RATE on parent channel if the 601 * requested value is not supported by the hardware. 602 */ 603 if (!err && feeder_rate_round && 604 (ch->feederflags & (1 << FEEDER_RATE))) { 605 newspd = sndbuf_getspd(ch->bufhard); 606 err = chn_setspeed(ch, newspd); 607 } 608 CHN_UNLOCK(ch); 609 if (err == 0) { 610 pcm_lock(d); 611 *vchanrate = newspd; 612 pcm_unlock(d); 613 } 614 } else 615 CHN_UNLOCK(ch); 616 } 617 pcm_inprog(d, -1); 618 return (err); 619} 620 621static int 622sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) 623{ 624 struct snddev_info *d; 625 struct pcm_channel *c, *ch = NULL; 626 uint32_t newfmt, spd; 627 int vchancount, *vchanformat; 628 int direction; 629 int err = 0, i; 630 char fmtstr[AFMTSTR_MAXSZ]; 631 632 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 633 if (d == NULL || !(d->flags & SD_F_AUTOVCHAN)) 634 return (EINVAL); 635 636 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 637 case VCHAN_PLAY: 638 direction = PCMDIR_PLAY; 639 vchancount = d->pvchancount; 640 vchanformat = &d->pvchanformat; 641 break; 642 case VCHAN_REC: 643 direction = PCMDIR_REC; 644 vchancount = d->rvchancount; 645 vchanformat = &d->rvchanformat; 646 break; 647 default: 648 return (EINVAL); 649 break; 650 } 651 652 if (vchancount < 1) 653 return (EINVAL); 654 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 655 pcm_inprog(d, -1); 656 return (EINPROGRESS); 657 } 658 CHN_FOREACH(c, d, channels.pcm) { 659 CHN_LOCK(c); 660 if (c->direction == direction) { 661 if (c->flags & CHN_F_VIRTUAL) { 662 /* Sanity check */ 663 if (ch != NULL && ch != c->parentchannel) { 664 CHN_UNLOCK(c); 665 pcm_inprog(d, -1); 666 return (EINVAL); 667 } 668 if (req->newptr != NULL && 669 (c->flags & CHN_F_BUSY)) { 670 CHN_UNLOCK(c); 671 pcm_inprog(d, -1); 672 return (EBUSY); 673 } 674 } else if (c->flags & CHN_F_HAS_VCHAN) { 675 /* No way!! */ 676 if (ch != NULL) { 677 CHN_UNLOCK(c); 678 pcm_inprog(d, -1); 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_inprog(d, -1); 695 return (EINVAL); 696 } 697 err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 698 if (err == 0 && req->newptr != NULL) { 699 for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) { 700 if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) { 701 strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, 702 sizeof(fmtstr)); 703 break; 704 } 705 } 706 newfmt = vchan_valid_strformat(fmtstr); 707 if (newfmt == 0) { 708 pcm_inprog(d, -1); 709 return (EINVAL); 710 } 711 CHN_LOCK(ch); 712 if (newfmt != ch->format) { 713 /* Get channel speed, before chn_reset() screw it. */ 714 spd = ch->speed; 715 err = chn_reset(ch, newfmt); 716 if (err == 0) 717 err = chn_setspeed(ch, spd); 718 CHN_UNLOCK(ch); 719 if (err == 0) { 720 pcm_lock(d); 721 *vchanformat = newfmt; 722 pcm_unlock(d); 723 } 724 } else 725 CHN_UNLOCK(ch); 726 } 727 pcm_inprog(d, -1); 728 return (err); 729} 730#endif 731 732/* virtual channel interface */ 733 734#define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 735 "play.vchanformat" : "rec.vchanformat" 736#define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 737 "play.vchanrate" : "rec.vchanrate" 738 739int 740vchan_create(struct pcm_channel *parent, int num) 741{ 742 struct snddev_info *d = parent->parentsnddev; 743 struct pcm_channel *ch, *tmp, *after; 744 struct pcmchan_caps *parent_caps; 745 uint32_t vchanfmt; 746 int err, first, speed, r; 747 int direction; 748 749 if (!(parent->flags & CHN_F_BUSY)) 750 return (EBUSY); 751 752 if (parent->direction == PCMDIR_PLAY) { 753 direction = PCMDIR_PLAY_VIRTUAL; 754 vchanfmt = d->pvchanformat; 755 speed = d->pvchanrate; 756 } else if (parent->direction == PCMDIR_REC) { 757 direction = PCMDIR_REC_VIRTUAL; 758 vchanfmt = d->rvchanformat; 759 speed = d->rvchanrate; 760 } else 761 return (EINVAL); 762 CHN_UNLOCK(parent); 763 764 /* create a new playback channel */ 765 ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent); 766 if (ch == NULL) { 767 CHN_LOCK(parent); 768 return (ENODEV); 769 } 770 771 /* add us to our grandparent's channel list */ 772 err = pcm_chn_add(d, ch); 773 if (err) { 774 pcm_chn_destroy(ch); 775 CHN_LOCK(parent); 776 return (err); 777 } 778 779 CHN_LOCK(parent); 780 /* add us to our parent channel's children */ 781 first = CHN_EMPTY(parent, children); 782 after = NULL; 783 CHN_FOREACH(tmp, parent, children) { 784 if (CHN_CHAN(tmp) > CHN_CHAN(ch)) 785 after = tmp; 786 else if (CHN_CHAN(tmp) < CHN_CHAN(ch)) 787 break; 788 } 789 if (after != NULL) { 790 CHN_INSERT_AFTER(after, ch, children); 791 } else { 792 CHN_INSERT_HEAD(parent, ch, children); 793 } 794 parent->flags |= CHN_F_HAS_VCHAN; 795 796 if (first) { 797 parent_caps = chn_getcaps(parent); 798 if (parent_caps == NULL) 799 err = EINVAL; 800 801 if (!err) { 802 if (vchanfmt == 0) { 803 const char *vfmt; 804 805 CHN_UNLOCK(parent); 806 r = resource_string_value( 807 device_get_name(parent->dev), 808 device_get_unit(parent->dev), 809 VCHAN_FMT_HINT(direction), 810 &vfmt); 811 CHN_LOCK(parent); 812 if (r != 0) 813 vfmt = NULL; 814 if (vfmt != NULL) { 815 vchanfmt = vchan_valid_strformat(vfmt); 816 for (r = 0; vchanfmt == 0 && 817 vchan_fmtstralias[r].alias != NULL; 818 r++) { 819 if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) { 820 vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr); 821 break; 822 } 823 } 824 } 825 if (vchanfmt == 0) 826 vchanfmt = VCHAN_DEFAULT_AFMT; 827 } 828 err = chn_reset(parent, vchanfmt); 829 } 830 831 if (!err) { 832 /* 833 * This is very sad. Few soundcards advertised as being 834 * able to do (insanely) higher/lower speed, but in 835 * reality, they simply can't. At least, we give user chance 836 * to set sane value via kernel hints or sysctl. 837 */ 838 if (speed < 1) { 839 CHN_UNLOCK(parent); 840 r = resource_int_value( 841 device_get_name(parent->dev), 842 device_get_unit(parent->dev), 843 VCHAN_SPD_HINT(direction), 844 &speed); 845 CHN_LOCK(parent); 846 if (r != 0) { 847 /* 848 * No saved value, no hint, NOTHING. 849 * 850 * Workaround for sb16 running 851 * poorly at 45k / 49k. 852 */ 853 switch (parent_caps->maxspeed) { 854 case 45000: 855 case 49000: 856 speed = 44100; 857 break; 858 default: 859 speed = VCHAN_DEFAULT_SPEED; 860 if (speed > parent_caps->maxspeed) 861 speed = parent_caps->maxspeed; 862 break; 863 } 864 if (speed < parent_caps->minspeed) 865 speed = parent_caps->minspeed; 866 } 867 } 868 869 if (feeder_rate_round) { 870 /* 871 * Limit speed based on driver caps. 872 * This is supposed to help fixed rate, non-VRA 873 * AC97 cards, but.. (see below) 874 */ 875 if (speed < parent_caps->minspeed) 876 speed = parent_caps->minspeed; 877 if (speed > parent_caps->maxspeed) 878 speed = parent_caps->maxspeed; 879 } 880 881 /* 882 * We still need to limit the speed between 883 * feeder_rate_min <-> feeder_rate_max. This is 884 * just an escape goat if all of the above failed 885 * miserably. 886 */ 887 if (speed < feeder_rate_min) 888 speed = feeder_rate_min; 889 if (speed > feeder_rate_max) 890 speed = feeder_rate_max; 891 892 err = chn_setspeed(parent, speed); 893 /* 894 * Try to avoid FEEDER_RATE on parent channel if the 895 * requested value is not supported by the hardware. 896 */ 897 if (!err && feeder_rate_round && 898 (parent->feederflags & (1 << FEEDER_RATE))) { 899 speed = sndbuf_getspd(parent->bufhard); 900 err = chn_setspeed(parent, speed); 901 } 902 903 if (!err) { 904 /* 905 * Save new value. 906 */ 907 CHN_UNLOCK(parent); 908 pcm_lock(d); 909 if (direction == PCMDIR_PLAY_VIRTUAL) { 910 d->pvchanformat = vchanfmt; 911 d->pvchanrate = speed; 912 } else { 913 d->rvchanformat = vchanfmt; 914 d->rvchanrate = speed; 915 } 916 pcm_unlock(d); 917 CHN_LOCK(parent); 918 } 919 } 920 921 if (err) { 922 CHN_REMOVE(parent, ch, children); 923 parent->flags &= ~CHN_F_HAS_VCHAN; 924 CHN_UNLOCK(parent); 925 pcm_lock(d); 926 if (pcm_chn_remove(d, ch) == 0) { 927 pcm_unlock(d); 928 pcm_chn_destroy(ch); 929 } else 930 pcm_unlock(d); 931 CHN_LOCK(parent); 932 return (err); 933 } 934 } 935 936 return (0); 937} 938 939int 940vchan_destroy(struct pcm_channel *c) 941{ 942 struct pcm_channel *parent = c->parentchannel; 943 struct snddev_info *d = parent->parentsnddev; 944 uint32_t spd; 945 int err; 946 947 CHN_LOCK(parent); 948 if (!(parent->flags & CHN_F_BUSY)) { 949 CHN_UNLOCK(parent); 950 return (EBUSY); 951 } 952 if (CHN_EMPTY(parent, children)) { 953 CHN_UNLOCK(parent); 954 return (EINVAL); 955 } 956 957 /* remove us from our parent's children list */ 958 CHN_REMOVE(parent, c, children); 959 960 if (CHN_EMPTY(parent, children)) { 961 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 962 spd = parent->speed; 963 if (chn_reset(parent, parent->format) == 0) 964 chn_setspeed(parent, spd); 965 } 966 967 CHN_UNLOCK(parent); 968 969 /* remove us from our grandparent's channel list */ 970 pcm_lock(d); 971 err = pcm_chn_remove(d, c); 972 pcm_unlock(d); 973 974 /* destroy ourselves */ 975 if (!err) 976 err = pcm_chn_destroy(c); 977 978 return (err); 979} 980 981int 982vchan_initsys(device_t dev) 983{ 984#ifdef SND_DYNSYSCTL 985 struct snddev_info *d; 986 int unit; 987 988 unit = device_get_unit(dev); 989 d = device_get_softc(dev); 990 991 /* Play */ 992 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 993 SYSCTL_CHILDREN(d->play_sysctl_tree), 994 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, 995 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 996 sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); 997 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 998 SYSCTL_CHILDREN(d->play_sysctl_tree), 999 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, 1000 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1001 sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate"); 1002 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1003 SYSCTL_CHILDREN(d->play_sysctl_tree), 1004 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, 1005 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1006 sysctl_hw_snd_vchanformat, "A", "virtual channel format"); 1007 /* Rec */ 1008 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1009 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1010 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, 1011 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1012 sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); 1013 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1014 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1015 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, 1016 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1017 sysctl_hw_snd_vchanrate, "I", "virtual channel base speed/rate"); 1018 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1019 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1020 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, 1021 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1022 sysctl_hw_snd_vchanformat, "A", "virtual channel format"); 1023#endif 1024 1025 return (0); 1026} 1027