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