channel.c revision 85406
1249423Sdim/* 2212793Sdim * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3212793Sdim * Portions Copyright by Luigi Rizzo - 1997-99 4212793Sdim * All rights reserved. 5212793Sdim * 6212793Sdim * Redistribution and use in source and binary forms, with or without 7212793Sdim * modification, are permitted provided that the following conditions 8212793Sdim * are met: 9212793Sdim * 1. Redistributions of source code must retain the above copyright 10212793Sdim * notice, this list of conditions and the following disclaimer. 11212793Sdim * 2. Redistributions in binary form must reproduce the above copyright 12212793Sdim * notice, this list of conditions and the following disclaimer in the 13212793Sdim * documentation and/or other materials provided with the distribution. 14212793Sdim * 15212793Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16212793Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17212793Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18212793Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19212793Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20212793Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21212793Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22212793Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23212793Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24212793Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25212793Sdim * SUCH DAMAGE. 26212793Sdim */ 27212793Sdim 28212793Sdim#include <dev/sound/pcm/sound.h> 29212793Sdim 30212793Sdim#include "feeder_if.h" 31212793Sdim 32212793SdimSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/channel.c 85406 2001-10-24 12:33:21Z cg $"); 33243830Sdim 34249423Sdim#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */ 35212793Sdim#define DMA_ALIGN_THRESHOLD 4 36239462Sdim#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1)) 37212793Sdim 38212793Sdim#define MIN(x, y) (((x) < (y))? (x) : (y)) 39243830Sdim#define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED)) 40212793Sdim 41243830Sdim/* 42239462Sdim#define DEB(x) x 43243830Sdim*/ 44239462Sdim 45212793Sdimstatic int chn_targetirqrate = 32; 46212793SdimTUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate); 47212793Sdim 48212793Sdimstatic int 49243830Sdimsysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS) 50243830Sdim{ 51212793Sdim int err, val; 52212793Sdim 53243830Sdim val = chn_targetirqrate; 54243830Sdim err = sysctl_handle_int(oidp, &val, sizeof(val), req); 55243830Sdim if (val < 16 || val > 512) 56243830Sdim err = EINVAL; 57243830Sdim else 58243830Sdim chn_targetirqrate = val; 59212793Sdim 60212793Sdim return err; 61212793Sdim} 62212793SdimSYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW, 63212793Sdim 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", ""); 64212793Sdim 65243830Sdimstatic int chn_buildfeeder(struct pcm_channel *c); 66243830Sdim 67243830Sdimstatic void 68212793Sdimchn_lockinit(struct pcm_channel *c) 69243830Sdim{ 70239462Sdim c->lock = snd_mtxcreate(c->name); 71212793Sdim} 72212793Sdim 73243830Sdimstatic void 74243830Sdimchn_lockdestroy(struct pcm_channel *c) 75212793Sdim{ 76212793Sdim snd_mtxfree(c->lock); 77212793Sdim} 78212793Sdim 79212793Sdimstatic int 80chn_polltrigger(struct pcm_channel *c) 81{ 82 struct snd_dbuf *bs = c->bufsoft; 83 unsigned amt, lim; 84 85 CHN_LOCKASSERT(c); 86 if (c->flags & CHN_F_MAPPED) { 87 if (sndbuf_getprevblocks(bs) == 0) 88 return 1; 89 else 90 return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0; 91 } else { 92 amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); 93 lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1; 94 lim = 1; 95 return (amt >= lim)? 1 : 0; 96 } 97 return 0; 98} 99 100static int 101chn_pollreset(struct pcm_channel *c) 102{ 103 struct snd_dbuf *bs = c->bufsoft; 104 105 CHN_LOCKASSERT(c); 106 sndbuf_updateprevtotal(bs); 107 return 1; 108} 109 110static void 111chn_wakeup(struct pcm_channel *c) 112{ 113 struct snd_dbuf *bs = c->bufsoft; 114 115 CHN_LOCKASSERT(c); 116 if (sndbuf_getsel(bs)->si_pid && chn_polltrigger(c)) 117 selwakeup(sndbuf_getsel(bs)); 118 wakeup(bs); 119} 120 121static int 122chn_sleep(struct pcm_channel *c, char *str, int timeout) 123{ 124 struct snd_dbuf *bs = c->bufsoft; 125 int ret; 126 127 CHN_LOCKASSERT(c); 128#ifdef USING_MUTEX 129 ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout); 130#else 131 ret = tsleep(bs, PRIBIO | PCATCH, str, timeout); 132#endif 133 134 return ret; 135} 136 137/* 138 * chn_dmaupdate() tracks the status of a dma transfer, 139 * updating pointers. It must be called at spltty(). 140 */ 141 142static unsigned int 143chn_dmaupdate(struct pcm_channel *c) 144{ 145 struct snd_dbuf *b = c->bufhard; 146 unsigned int delta, old, hwptr, amt; 147 148 KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0")); 149 CHN_LOCKASSERT(c); 150 old = sndbuf_gethwptr(b); 151 hwptr = chn_getptr(c); 152 delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b); 153 sndbuf_sethwptr(b, hwptr); 154 155 DEB( 156 if (delta >= ((sndbuf_getsize(b) * 15) / 16)) { 157 if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING))) 158 device_printf(c->parentsnddev->dev, "hwptr went backwards %d -> %d\n", old, hwptr); 159 } 160 ); 161 162 if (c->direction == PCMDIR_PLAY) { 163 amt = MIN(delta, sndbuf_getready(b)); 164 if (amt > 0) 165 sndbuf_dispose(b, NULL, amt); 166 } else { 167 amt = MIN(delta, sndbuf_getfree(b)); 168 if (amt > 0) 169 sndbuf_acquire(b, NULL, amt); 170 } 171 172 return delta; 173} 174 175void 176chn_wrupdate(struct pcm_channel *c) 177{ 178 int ret; 179 180 CHN_LOCKASSERT(c); 181 KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); 182 183 if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED)) 184 return; 185 chn_dmaupdate(c); 186 ret = chn_wrfeed(c); 187 /* tell the driver we've updated the primary buffer */ 188 chn_trigger(c, PCMTRIG_EMLDMAWR); 189 DEB(if (ret) 190 printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);) 191 192} 193 194static int irqc = 0; 195 196int 197chn_wrfeed(struct pcm_channel *c) 198{ 199 struct snd_dbuf *b = c->bufhard; 200 struct snd_dbuf *bs = c->bufsoft; 201 unsigned int ret, amt; 202 203 CHN_LOCKASSERT(c); 204 DEB( 205 if (c->flags & CHN_F_CLOSING) { 206 sndbuf_dump(b, "b", 0x02); 207 sndbuf_dump(bs, "bs", 0x02); 208 }) 209 210 amt = sndbuf_getfree(b); 211 if (sndbuf_getready(bs) < amt) 212 c->xruns++; 213 ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC; 214 if (ret == 0 && sndbuf_getfree(b) < amt) 215 chn_wakeup(c); 216/* 217 if (!(irqc & 63) || (ret != 0)) 218 sndbuf_dump(b, "b:wrfeed", 0x03); 219*/ 220 return ret; 221} 222 223static void 224chn_wrintr(struct pcm_channel *c) 225{ 226 int ret; 227 228 CHN_LOCKASSERT(c); 229 irqc++; 230 /* update pointers in primary buffer */ 231 chn_dmaupdate(c); 232 /* ...and feed from secondary to primary */ 233 ret = chn_wrfeed(c); 234 /* tell the driver we've updated the primary buffer */ 235 chn_trigger(c, PCMTRIG_EMLDMAWR); 236 DEB(if (ret) 237 printf("chn_wrintr: chn_wrfeed returned %d\n", ret);) 238} 239 240/* 241 * user write routine - uiomove data into secondary buffer, trigger if necessary 242 * if blocking, sleep, rinse and repeat. 243 * 244 * called externally, so must handle locking 245 */ 246 247int 248chn_write(struct pcm_channel *c, struct uio *buf) 249{ 250 int ret, timeout, newsize, count, sz; 251 struct snd_dbuf *bs = c->bufsoft; 252 253 CHN_LOCKASSERT(c); 254 /* 255 * XXX Certain applications attempt to write larger size 256 * of pcm data than c->blocksize2nd without blocking, 257 * resulting partial write. Expand the block size so that 258 * the write operation avoids blocking. 259 */ 260 if ((c->flags & CHN_F_NBIO) && buf->uio_resid > sndbuf_getblksz(bs)) { 261 DEB(device_printf(c->parentsnddev->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n", 262 buf->uio_resid, sndbuf_getblksz(bs))); 263 newsize = 16; 264 while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2)) 265 newsize <<= 1; 266 chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize); 267 DEB(device_printf(c->parentsnddev->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs))); 268 } 269 270 ret = 0; 271 count = hz; 272 while (!ret && (buf->uio_resid > 0) && (count > 0)) { 273 sz = sndbuf_getfree(bs); 274 if (sz == 0) { 275 if (c->flags & CHN_F_NBIO) 276 ret = EWOULDBLOCK; 277 else { 278 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); 279 if (timeout < 1) 280 timeout = 1; 281 timeout = 1; 282 ret = chn_sleep(c, "pcmwr", timeout); 283 if (ret == EWOULDBLOCK) { 284 count -= timeout; 285 ret = 0; 286 } else if (ret == 0) 287 count = hz; 288 } 289 } else { 290 sz = MIN(sz, buf->uio_resid); 291 KASSERT(sz > 0, ("confusion in chn_write")); 292 /* printf("sz: %d\n", sz); */ 293 ret = sndbuf_uiomove(bs, buf, sz); 294 if (ret == 0 && !(c->flags & CHN_F_TRIGGERED)) 295 chn_start(c, 0); 296 } 297 } 298 /* printf("ret: %d left: %d\n", ret, buf->uio_resid); */ 299 300 if (count <= 0) { 301 c->flags |= CHN_F_DEAD; 302 printf("%s: play interrupt timeout, channel dead\n", c->name); 303 } 304 305 return ret; 306} 307 308static int 309chn_rddump(struct pcm_channel *c, unsigned int cnt) 310{ 311 struct snd_dbuf *b = c->bufhard; 312 313 CHN_LOCKASSERT(c); 314 sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt); 315 return sndbuf_dispose(b, NULL, cnt); 316} 317 318/* 319 * Feed new data from the read buffer. Can be called in the bottom half. 320 * Hence must be called at spltty. 321 */ 322int 323chn_rdfeed(struct pcm_channel *c) 324{ 325 struct snd_dbuf *b = c->bufhard; 326 struct snd_dbuf *bs = c->bufsoft; 327 unsigned int ret, amt; 328 329 CHN_LOCKASSERT(c); 330 DEB( 331 if (c->flags & CHN_F_CLOSING) { 332 sndbuf_dump(b, "b", 0x02); 333 sndbuf_dump(bs, "bs", 0x02); 334 }) 335 336 amt = sndbuf_getready(b); 337 if (sndbuf_getfree(bs) < amt) { 338 c->xruns++; 339 amt = sndbuf_getfree(bs); 340 } 341 ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0; 342 343 amt = sndbuf_getready(b); 344 if (amt > 0) 345 chn_rddump(c, amt); 346 347 chn_wakeup(c); 348 349 return ret; 350} 351 352void 353chn_rdupdate(struct pcm_channel *c) 354{ 355 int ret; 356 357 CHN_LOCKASSERT(c); 358 KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel")); 359 360 if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED)) 361 return; 362 chn_trigger(c, PCMTRIG_EMLDMARD); 363 chn_dmaupdate(c); 364 ret = chn_rdfeed(c); 365 if (ret) 366 printf("chn_rdfeed: %d\n", ret); 367 368} 369 370/* read interrupt routine. Must be called with interrupts blocked. */ 371static void 372chn_rdintr(struct pcm_channel *c) 373{ 374 int ret; 375 376 CHN_LOCKASSERT(c); 377 /* tell the driver to update the primary buffer if non-dma */ 378 chn_trigger(c, PCMTRIG_EMLDMARD); 379 /* update pointers in primary buffer */ 380 chn_dmaupdate(c); 381 /* ...and feed from primary to secondary */ 382 ret = chn_rdfeed(c); 383} 384 385/* 386 * user read routine - trigger if necessary, uiomove data from secondary bufhard 387 * if blocking, sleep, rinse and repeat. 388 * 389 * called externally, so must handle locking 390 */ 391 392int 393chn_read(struct pcm_channel *c, struct uio *buf) 394{ 395 int ret, timeout, sz, count; 396 struct snd_dbuf *bs = c->bufsoft; 397 398 CHN_LOCKASSERT(c); 399 if (!(c->flags & CHN_F_TRIGGERED)) 400 chn_start(c, 0); 401 402 ret = 0; 403 count = hz; 404 while (!ret && (buf->uio_resid > 0) && (count > 0)) { 405 sz = MIN(buf->uio_resid, sndbuf_getready(bs)); 406 407 if (sz > 0) { 408 ret = sndbuf_uiomove(bs, buf, sz); 409 } else { 410 if (c->flags & CHN_F_NBIO) { 411 ret = EWOULDBLOCK; 412 } else { 413 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); 414 if (timeout < 1) 415 timeout = 1; 416 ret = chn_sleep(c, "pcmrd", timeout); 417 if (ret == EWOULDBLOCK) { 418 count -= timeout; 419 ret = 0; 420 } else { 421 count = hz; 422 } 423 424 } 425 } 426 } 427 428 if (count <= 0) { 429 c->flags |= CHN_F_DEAD; 430 printf("%s: record interrupt timeout, channel dead\n", c->name); 431 } 432 433 return ret; 434} 435 436void 437chn_intr(struct pcm_channel *c) 438{ 439 CHN_LOCK(c); 440 c->interrupts++; 441 if (c->direction == PCMDIR_PLAY) 442 chn_wrintr(c); 443 else 444 chn_rdintr(c); 445 CHN_UNLOCK(c); 446} 447 448u_int32_t 449chn_start(struct pcm_channel *c, int force) 450{ 451 u_int32_t i, j; 452 struct snd_dbuf *b = c->bufhard; 453 struct snd_dbuf *bs = c->bufsoft; 454 455 CHN_LOCKASSERT(c); 456 /* if we're running, or if we're prevented from triggering, bail */ 457 if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force)) 458 return EINVAL; 459 460 i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs); 461 j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b); 462 if (force || (i >= j)) { 463 c->flags |= CHN_F_TRIGGERED; 464 /* 465 * if we're starting because a vchan started, don't feed any data 466 * or it becomes impossible to start vchans synchronised with the 467 * first one. the hardbuf should be empty so we top it up with 468 * silence to give it something to chew. the real data will be 469 * fed at the first irq. 470 */ 471 if (c->direction == PCMDIR_PLAY) { 472 if (SLIST_EMPTY(&c->children)) 473 chn_wrfeed(c); 474 else 475 sndbuf_fillsilence(b); 476 } 477 sndbuf_setrun(b, 1); 478 c->xruns = 0; 479 chn_trigger(c, PCMTRIG_START); 480 return 0; 481 } 482 483 return 0; 484} 485 486void 487chn_resetbuf(struct pcm_channel *c) 488{ 489 struct snd_dbuf *b = c->bufhard; 490 struct snd_dbuf *bs = c->bufsoft; 491 492 c->blocks = 0; 493 sndbuf_reset(b); 494 sndbuf_reset(bs); 495} 496 497/* 498 * chn_sync waits until the space in the given channel goes above 499 * a threshold. The threshold is checked against fl or rl respectively. 500 * Assume that the condition can become true, do not check here... 501 */ 502int 503chn_sync(struct pcm_channel *c, int threshold) 504{ 505 u_long rdy; 506 int ret; 507 struct snd_dbuf *bs = c->bufsoft; 508 509 CHN_LOCKASSERT(c); 510 for (;;) { 511 rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); 512 if (rdy <= threshold) { 513 ret = chn_sleep(c, "pcmsyn", 1); 514 if (ret == ERESTART || ret == EINTR) { 515 DEB(printf("chn_sync: tsleep returns %d\n", ret)); 516 return -1; 517 } 518 } else 519 break; 520 } 521 return 0; 522} 523 524/* called externally, handle locking */ 525int 526chn_poll(struct pcm_channel *c, int ev, struct thread *td) 527{ 528 struct snd_dbuf *bs = c->bufsoft; 529 int ret; 530 531 CHN_LOCKASSERT(c); 532 if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED)) 533 chn_start(c, 1); 534 ret = 0; 535 if (chn_polltrigger(c) && chn_pollreset(c)) 536 ret = ev; 537 else 538 selrecord(td, sndbuf_getsel(bs)); 539 return ret; 540} 541 542/* 543 * chn_abort terminates a running dma transfer. it may sleep up to 200ms. 544 * it returns the number of bytes that have not been transferred. 545 * 546 * called from: dsp_close, dsp_ioctl, with channel locked 547 */ 548int 549chn_abort(struct pcm_channel *c) 550{ 551 int missing = 0; 552 struct snd_dbuf *b = c->bufhard; 553 struct snd_dbuf *bs = c->bufsoft; 554 555 CHN_LOCKASSERT(c); 556 if (!(c->flags & CHN_F_TRIGGERED)) 557 return 0; 558 c->flags |= CHN_F_ABORTING; 559 560 c->flags &= ~CHN_F_TRIGGERED; 561 /* kill the channel */ 562 chn_trigger(c, PCMTRIG_ABORT); 563 sndbuf_setrun(b, 0); 564 if (!(c->flags & CHN_F_VIRTUAL)) 565 chn_dmaupdate(c); 566 missing = sndbuf_getready(bs) + sndbuf_getready(b); 567 568 c->flags &= ~CHN_F_ABORTING; 569 return missing; 570} 571 572/* 573 * this routine tries to flush the dma transfer. It is called 574 * on a close. We immediately abort any read DMA 575 * operation, and then wait for the play bufhard to drain. 576 * 577 * called from: dsp_close 578 */ 579 580int 581chn_flush(struct pcm_channel *c) 582{ 583 int ret, count, resid, resid_p; 584 struct snd_dbuf *b = c->bufhard; 585 struct snd_dbuf *bs = c->bufsoft; 586 587 CHN_LOCKASSERT(c); 588 KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); 589 DEB(printf("chn_flush c->flags 0x%08x\n", c->flags)); 590 if (!(c->flags & CHN_F_TRIGGERED)) 591 return 0; 592 593 c->flags |= CHN_F_CLOSING; 594 resid = sndbuf_getready(bs) + sndbuf_getready(b); 595 resid_p = resid; 596 count = 10; 597 ret = 0; 598 while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) { 599 /* still pending output data. */ 600 ret = chn_sleep(c, "pcmflu", hz / 10); 601 if (ret == EWOULDBLOCK) 602 ret = 0; 603 if (ret == 0) { 604 resid = sndbuf_getready(bs) + sndbuf_getready(b); 605 if (resid >= resid_p) 606 count--; 607 resid_p = resid; 608 } 609 } 610 if (count == 0) 611 DEB(printf("chn_flush: timeout\n")); 612 613 c->flags &= ~CHN_F_TRIGGERED; 614 /* kill the channel */ 615 chn_trigger(c, PCMTRIG_ABORT); 616 sndbuf_setrun(b, 0); 617 618 c->flags &= ~CHN_F_CLOSING; 619 return 0; 620} 621 622int 623fmtvalid(u_int32_t fmt, u_int32_t *fmtlist) 624{ 625 int i; 626 627 for (i = 0; fmtlist[i]; i++) 628 if (fmt == fmtlist[i]) 629 return 1; 630 return 0; 631} 632 633int 634chn_reset(struct pcm_channel *c, u_int32_t fmt) 635{ 636 int hwspd, r = 0; 637 638 CHN_LOCKASSERT(c); 639 c->flags &= CHN_F_RESET; 640 c->interrupts = 0; 641 c->xruns = 0; 642 CHANNEL_RESET(c->methods, c->devinfo); 643 if (fmt) { 644 hwspd = DSP_DEFAULT_SPEED; 645 /* only do this on a record channel until feederbuilder works */ 646 if (c->direction == PCMDIR_REC) 647 RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); 648 c->speed = hwspd; 649 650 r = chn_setformat(c, fmt); 651 if (r == 0) 652 r = chn_setspeed(c, hwspd); 653 if (r == 0) 654 r = chn_setvolume(c, 100, 100); 655 } 656 r = chn_setblocksize(c, 0, 0); 657 if (r == 0) { 658 chn_resetbuf(c); 659 CHANNEL_RESETDONE(c->methods, c->devinfo); 660 } 661 return r; 662} 663 664int 665chn_init(struct pcm_channel *c, void *devinfo, int dir) 666{ 667 struct feeder_class *fc; 668 struct snd_dbuf *b, *bs; 669 670 chn_lockinit(c); 671 CHN_LOCK(c); 672 673 c->feeder = NULL; 674 fc = feeder_getclass(NULL); 675 if (fc == NULL) 676 return EINVAL; 677 if (chn_addfeeder(c, fc, NULL)) 678 return EINVAL; 679 680 b = sndbuf_create(c->name, "primary"); 681 if (b == NULL) 682 return ENOMEM; 683 bs = sndbuf_create(c->name, "secondary"); 684 if (bs == NULL) { 685 sndbuf_destroy(b); 686 return ENOMEM; 687 } 688 sndbuf_setup(bs, NULL, 0); 689 c->bufhard = b; 690 c->bufsoft = bs; 691 c->flags = 0; 692 c->feederflags = 0; 693 c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir); 694 if (c->devinfo == NULL) { 695 sndbuf_destroy(bs); 696 sndbuf_destroy(b); 697 return ENODEV; 698 } 699 if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) { 700 sndbuf_destroy(bs); 701 sndbuf_destroy(b); 702 return ENOMEM; 703 } 704 chn_setdir(c, dir); 705 706 /* And the secondary buffer. */ 707 sndbuf_setfmt(b, AFMT_U8); 708 sndbuf_setfmt(bs, AFMT_U8); 709 CHN_UNLOCK(c); 710 return 0; 711} 712 713int 714chn_kill(struct pcm_channel *c) 715{ 716 struct snd_dbuf *b = c->bufhard; 717 struct snd_dbuf *bs = c->bufsoft; 718 719 CHN_LOCK(c); 720 if (c->flags & CHN_F_TRIGGERED) 721 chn_trigger(c, PCMTRIG_ABORT); 722 while (chn_removefeeder(c) == 0); 723 if (CHANNEL_FREE(c->methods, c->devinfo)) 724 sndbuf_free(c->bufhard); 725 c->flags |= CHN_F_DEAD; 726 sndbuf_destroy(bs); 727 sndbuf_destroy(b); 728 chn_lockdestroy(c); 729 return 0; 730} 731 732int 733chn_setdir(struct pcm_channel *c, int dir) 734{ 735 struct snd_dbuf *b = c->bufhard; 736 int r; 737 738 CHN_LOCKASSERT(c); 739 c->direction = dir; 740 r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); 741 if (!r && ISA_DMA(b)) 742 sndbuf_isadmasetdir(b, c->direction); 743 return r; 744} 745 746int 747chn_setvolume(struct pcm_channel *c, int left, int right) 748{ 749 CHN_LOCKASSERT(c); 750 /* could add a feeder for volume changing if channel returns -1 */ 751 c->volume = (left << 8) | right; 752 return 0; 753} 754 755static int 756chn_tryspeed(struct pcm_channel *c, int speed) 757{ 758 struct pcm_feeder *f; 759 struct snd_dbuf *b = c->bufhard; 760 struct snd_dbuf *bs = c->bufsoft; 761 int r, delta; 762 763 CHN_LOCKASSERT(c); 764 DEB(printf("setspeed, channel %s\n", c->name)); 765 DEB(printf("want speed %d, ", speed)); 766 if (speed <= 0) 767 return EINVAL; 768 if (CANCHANGE(c)) { 769 r = 0; 770 c->speed = speed; 771 sndbuf_setspd(bs, speed); 772 RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); 773 DEB(printf("try speed %d, ", speed)); 774 sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed)); 775 DEB(printf("got speed %d\n", sndbuf_getspd(b))); 776 777 delta = sndbuf_getspd(b) - sndbuf_getspd(bs); 778 if (delta < 0) 779 delta = -delta; 780 781 c->feederflags &= ~(1 << FEEDER_RATE); 782 if (delta > 500) 783 c->feederflags |= 1 << FEEDER_RATE; 784 else 785 sndbuf_setspd(bs, sndbuf_getspd(b)); 786 787 r = chn_buildfeeder(c); 788 DEB(printf("r = %d\n", r)); 789 if (r) 790 goto out; 791 792 r = chn_setblocksize(c, 0, 0); 793 if (r) 794 goto out; 795 796 if (!(c->feederflags & (1 << FEEDER_RATE))) 797 goto out; 798 799 r = EINVAL; 800 f = chn_findfeeder(c, FEEDER_RATE); 801 DEB(printf("feedrate = %p\n", f)); 802 if (f == NULL) 803 goto out; 804 805 r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(bs)); 806 DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(bs), r)); 807 if (r) 808 goto out; 809 810 r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(b)); 811 DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(b), r)); 812out: 813 DEB(printf("setspeed done, r = %d\n", r)); 814 return r; 815 } else 816 return EINVAL; 817} 818 819int 820chn_setspeed(struct pcm_channel *c, int speed) 821{ 822 int r, oldspeed = c->speed; 823 824 r = chn_tryspeed(c, speed); 825 if (r) { 826 DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed)); 827 chn_tryspeed(c, oldspeed); 828 } 829 return r; 830} 831 832static int 833chn_tryformat(struct pcm_channel *c, u_int32_t fmt) 834{ 835 struct snd_dbuf *b = c->bufhard; 836 struct snd_dbuf *bs = c->bufsoft; 837 int r; 838 839 CHN_LOCKASSERT(c); 840 if (CANCHANGE(c)) { 841 DEB(printf("want format %d\n", fmt)); 842 c->format = fmt; 843 r = chn_buildfeeder(c); 844 if (r == 0) { 845 sndbuf_setfmt(b, c->feeder->desc->out); 846 sndbuf_setfmt(bs, fmt); 847 chn_resetbuf(c); 848 CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b)); 849 r = chn_tryspeed(c, c->speed); 850 } 851 return r; 852 } else 853 return EINVAL; 854} 855 856int 857chn_setformat(struct pcm_channel *c, u_int32_t fmt) 858{ 859 u_int32_t oldfmt = c->format; 860 int r; 861 862 r = chn_tryformat(c, fmt); 863 if (r) { 864 DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt)); 865 chn_tryformat(c, oldfmt); 866 } 867 return r; 868} 869 870int 871chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz) 872{ 873 struct snd_dbuf *b = c->bufhard; 874 struct snd_dbuf *bs = c->bufsoft; 875 int bufsz, irqhz, tmp, ret; 876 877 CHN_LOCKASSERT(c); 878 if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) 879 return EINVAL; 880 881 ret = 0; 882 DEB(printf("%s(%d, %d)\n", __FUNCTION__, blkcnt, blksz)); 883 if (blksz == 0 || blksz == -1) { 884 if (blksz == -1) 885 c->flags &= ~CHN_F_HAS_SIZE; 886 if (!(c->flags & CHN_F_HAS_SIZE)) { 887 blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate; 888 tmp = 32; 889 while (tmp <= blksz) 890 tmp <<= 1; 891 tmp >>= 1; 892 blksz = tmp; 893 blkcnt = CHN_2NDBUFMAXSIZE / blksz; 894 895 RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2); 896 RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz); 897 DEB(printf("%s: defaulting to (%d, %d)\n", __FUNCTION__, blkcnt, blksz)); 898 } else { 899 blkcnt = sndbuf_getblkcnt(bs); 900 blksz = sndbuf_getblksz(bs); 901 DEB(printf("%s: updating (%d, %d)\n", __FUNCTION__, blkcnt, blksz)); 902 } 903 } else { 904 ret = EINVAL; 905 if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE)) 906 goto out; 907 ret = 0; 908 c->flags |= CHN_F_HAS_SIZE; 909 } 910 911 bufsz = blkcnt * blksz; 912 913 ret = ENOMEM; 914 if (sndbuf_remalloc(bs, blkcnt, blksz)) 915 goto out; 916 ret = 0; 917 918 /* adjust for different hw format/speed */ 919 irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs); 920 DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __FUNCTION__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz)); 921 RANGE(irqhz, 16, 512); 922 923 sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz); 924 925 /* round down to 2^x */ 926 blksz = 32; 927 while (blksz <= sndbuf_getblksz(b)) 928 blksz <<= 1; 929 blksz >>= 1; 930 931 /* round down to fit hw bufhard size */ 932 RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2); 933 DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __FUNCTION__, blksz, sndbuf_getmaxsize(b))); 934 935 sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz)); 936 937 irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b); 938 DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz)); 939 940 chn_resetbuf(c); 941out: 942 return ret; 943} 944 945int 946chn_trigger(struct pcm_channel *c, int go) 947{ 948 struct snd_dbuf *b = c->bufhard; 949 int ret; 950 951 CHN_LOCKASSERT(c); 952 if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)) 953 sndbuf_isadmabounce(b); 954 ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go); 955 956 return ret; 957} 958 959int 960chn_getptr(struct pcm_channel *c) 961{ 962 int hwptr; 963 int a = (1 << c->align) - 1; 964 965 CHN_LOCKASSERT(c); 966 hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; 967 /* don't allow unaligned values in the hwa ptr */ 968#if 1 969 hwptr &= ~a ; /* Apply channel align mask */ 970#endif 971 hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ 972 return hwptr; 973} 974 975struct pcmchan_caps * 976chn_getcaps(struct pcm_channel *c) 977{ 978 CHN_LOCKASSERT(c); 979 return CHANNEL_GETCAPS(c->methods, c->devinfo); 980} 981 982u_int32_t 983chn_getformats(struct pcm_channel *c) 984{ 985 u_int32_t *fmtlist, fmts; 986 int i; 987 988 fmtlist = chn_getcaps(c)->fmtlist; 989 fmts = 0; 990 for (i = 0; fmtlist[i]; i++) 991 fmts |= fmtlist[i]; 992 993 return fmts; 994} 995 996static int 997chn_buildfeeder(struct pcm_channel *c) 998{ 999 struct feeder_class *fc; 1000 struct pcm_feederdesc desc; 1001 u_int32_t tmp[2], type, flags; 1002 1003 CHN_LOCKASSERT(c); 1004 while (chn_removefeeder(c) == 0); 1005 KASSERT((c->feeder == NULL), ("feeder chain not empty")); 1006 1007 c->align = sndbuf_getalign(c->bufsoft); 1008 1009 if (SLIST_EMPTY(&c->children)) { 1010 fc = feeder_getclass(NULL); 1011 if (fc == NULL) { 1012 DEB(printf("can't find root feeder\n")); 1013 return EINVAL; 1014 } 1015 if (chn_addfeeder(c, fc, NULL)) { 1016 DEB(printf("can't add root feeder\n")); 1017 return EINVAL; 1018 } 1019 c->feeder->desc->out = c->format; 1020 } else { 1021 desc.type = FEEDER_MIXER; 1022 desc.in = 0; 1023 desc.out = c->format; 1024 desc.flags = 0; 1025 fc = feeder_getclass(&desc); 1026 if (fc == NULL) { 1027 DEB(printf("can't find vchan feeder\n")); 1028 return EINVAL; 1029 } 1030 if (chn_addfeeder(c, fc, &desc)) { 1031 DEB(printf("can't add vchan feeder\n")); 1032 return EINVAL; 1033 } 1034 } 1035 flags = c->feederflags; 1036 1037 if ((c->flags & CHN_F_MAPPED) && (flags != 0)) { 1038 DEB(printf("can't build feeder chain on mapped channel\n")); 1039 return EINVAL; 1040 } 1041 DEB(printf("not mapped, flags %x\n", flags)); 1042 1043 for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) { 1044 if (flags & (1 << type)) { 1045 desc.type = type; 1046 desc.in = 0; 1047 desc.out = 0; 1048 desc.flags = 0; 1049 DEB(printf("find feeder type %d, ", type)); 1050 fc = feeder_getclass(&desc); 1051 DEB(printf("got %p\n", fc)); 1052 if (fc == NULL) { 1053 DEB(printf("can't find required feeder type %d\n", type)); 1054 return EINVAL; 1055 } 1056 1057 if (c->feeder->desc->out != fc->desc->in) { 1058 DEB(printf("build fmtchain from %x to %x: ", c->feeder->desc->out, fc->desc->in)); 1059 tmp[0] = fc->desc->in; 1060 tmp[1] = 0; 1061 if (chn_fmtchain(c, tmp) == 0) { 1062 DEB(printf("failed\n")); 1063 return EINVAL; 1064 } 1065 DEB(printf("ok\n")); 1066 } 1067 1068 if (chn_addfeeder(c, fc, fc->desc)) { 1069 DEB(printf("can't add feeder %p, output %x\n", fc, fc->desc->out)); 1070 return EINVAL; 1071 } 1072 DEB(printf("added feeder %p, output %x\n", fc, c->feeder->desc->out)); 1073 } 1074 } 1075 1076 if (!fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) { 1077 if (chn_fmtchain(c, chn_getcaps(c)->fmtlist) == 0) { 1078 DEB(printf("can't build fmtchain from %x\n", c->feeder->desc->out)); 1079 return EINVAL; 1080 } 1081 DEB(printf("built fmtchain from %x\n", c->feeder->desc->out)); 1082 } 1083 1084 return 0; 1085} 1086 1087int 1088chn_notify(struct pcm_channel *c, u_int32_t flags) 1089{ 1090 struct pcmchan_children *pce; 1091 struct pcm_channel *child; 1092 int run; 1093 1094 if (SLIST_EMPTY(&c->children)) 1095 return ENODEV; 1096 1097 run = (c->flags & CHN_F_TRIGGERED)? 1 : 0; 1098 /* 1099 * if the hwchan is running, we can't change its rate, format or 1100 * blocksize 1101 */ 1102 if (run) 1103 flags &= CHN_N_VOLUME | CHN_N_TRIGGER; 1104 1105 if (flags & CHN_N_RATE) { 1106 /* 1107 * we could do something here, like scan children and decide on 1108 * the most appropriate rate to mix at, but we don't for now 1109 */ 1110 } 1111 if (flags & CHN_N_FORMAT) { 1112 /* 1113 * we could do something here, like scan children and decide on 1114 * the most appropriate mixer feeder to use, but we don't for now 1115 */ 1116 } 1117 if (flags & CHN_N_VOLUME) { 1118 /* 1119 * we could do something here but we don't for now 1120 */ 1121 } 1122 if (flags & CHN_N_BLOCKSIZE) { 1123 int blksz; 1124 /* 1125 * scan the children, find the lowest blocksize and use that 1126 * for the hard blocksize 1127 */ 1128 blksz = sndbuf_getmaxsize(c->bufhard) / 2; 1129 SLIST_FOREACH(pce, &c->children, link) { 1130 child = pce->channel; 1131 if (sndbuf_getblksz(child->bufhard) < blksz) 1132 blksz = sndbuf_getblksz(child->bufhard); 1133 } 1134 chn_setblocksize(c, 2, blksz); 1135 } 1136 if (flags & CHN_N_TRIGGER) { 1137 int nrun; 1138 /* 1139 * scan the children, and figure out if any are running 1140 * if so, we need to be running, otherwise we need to be stopped 1141 * if we aren't in our target sstate, move to it 1142 */ 1143 nrun = 0; 1144 SLIST_FOREACH(pce, &c->children, link) { 1145 child = pce->channel; 1146 if (child->flags & CHN_F_TRIGGERED) 1147 nrun = 1; 1148 } 1149 if (nrun && !run) 1150 chn_start(c, 1); 1151 if (!nrun && run) 1152 chn_abort(c); 1153 } 1154 return 0; 1155} 1156