channel.c revision 78362
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 78362 2001-06-16 21:25:10Z 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 | CHN_F_VIRTUAL)) || !(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_LOCKASSERT(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 return ret; 502} 503 504/* 505 * chn_abort terminates a running dma transfer. it may sleep up to 200ms. 506 * it returns the number of bytes that have not been transferred. 507 * 508 * called from: dsp_close, dsp_ioctl, with channel locked 509 */ 510int 511chn_abort(struct pcm_channel *c) 512{ 513 int missing = 0; 514 struct snd_dbuf *b = c->bufhard; 515 struct snd_dbuf *bs = c->bufsoft; 516 517 CHN_LOCKASSERT(c); 518 if (!(c->flags & CHN_F_TRIGGERED)) 519 return 0; 520 c->flags |= CHN_F_ABORTING; 521 522 c->flags &= ~CHN_F_TRIGGERED; 523 /* kill the channel */ 524 chn_trigger(c, PCMTRIG_ABORT); 525 sndbuf_setrun(b, 0); 526 if (!(c->flags & CHN_F_VIRTUAL)) 527 chn_dmaupdate(c); 528 missing = sndbuf_getready(bs) + sndbuf_getready(b); 529 530 c->flags &= ~CHN_F_ABORTING; 531 return missing; 532} 533 534/* 535 * this routine tries to flush the dma transfer. It is called 536 * on a close. We immediately abort any read DMA 537 * operation, and then wait for the play bufhard to drain. 538 * 539 * called from: dsp_close 540 */ 541 542int 543chn_flush(struct pcm_channel *c) 544{ 545 int ret, count, resid, resid_p; 546 struct snd_dbuf *b = c->bufhard; 547 struct snd_dbuf *bs = c->bufsoft; 548 549 CHN_LOCKASSERT(c); 550 KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); 551 DEB(printf("chn_flush c->flags 0x%08x\n", c->flags)); 552 if (!(c->flags & CHN_F_TRIGGERED)) 553 return 0; 554 555 c->flags |= CHN_F_CLOSING; 556 resid = sndbuf_getready(bs) + sndbuf_getready(b); 557 resid_p = resid; 558 count = 10; 559 ret = 0; 560 while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) { 561 /* still pending output data. */ 562 ret = chn_sleep(c, "pcmflu", hz / 10); 563 if (ret == EWOULDBLOCK) 564 ret = 0; 565 if (ret == 0) { 566 resid = sndbuf_getready(bs) + sndbuf_getready(b); 567 if (resid >= resid_p) 568 count--; 569 resid_p = resid; 570 } 571 } 572 if (count == 0) 573 DEB(printf("chn_flush: timeout\n")); 574 575 c->flags &= ~CHN_F_TRIGGERED; 576 /* kill the channel */ 577 chn_trigger(c, PCMTRIG_ABORT); 578 sndbuf_setrun(b, 0); 579 580 c->flags &= ~CHN_F_CLOSING; 581 return 0; 582} 583 584int 585fmtvalid(u_int32_t fmt, u_int32_t *fmtlist) 586{ 587 int i; 588 589 for (i = 0; fmtlist[i]; i++) 590 if (fmt == fmtlist[i]) 591 return 1; 592 return 0; 593} 594 595int 596chn_reset(struct pcm_channel *c, u_int32_t fmt) 597{ 598 int hwspd, r = 0; 599 600 CHN_LOCKASSERT(c); 601 c->flags &= CHN_F_RESET; 602 CHANNEL_RESET(c->methods, c->devinfo); 603 if (fmt) { 604 hwspd = DSP_DEFAULT_SPEED; 605 RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); 606 c->speed = hwspd; 607 608 r = chn_setformat(c, fmt); 609 if (r == 0) 610 r = chn_setspeed(c, hwspd); 611 if (r == 0) 612 r = chn_setvolume(c, 100, 100); 613 } 614 r = chn_setblocksize(c, 0, 0); 615 if (r == 0) { 616 chn_resetbuf(c); 617 CHANNEL_RESETDONE(c->methods, c->devinfo); 618 } 619 return r; 620} 621 622int 623chn_init(struct pcm_channel *c, void *devinfo, int dir) 624{ 625 struct feeder_class *fc; 626 struct snd_dbuf *b, *bs; 627 628 chn_lockinit(c); 629 CHN_LOCK(c); 630 /* Initialize the hardware and DMA bufhard first. */ 631 c->feeder = NULL; 632 fc = feeder_getclass(NULL); 633 if (fc == NULL) 634 return EINVAL; 635 if (chn_addfeeder(c, fc, NULL)) 636 return EINVAL; 637 638 b = sndbuf_create(c->name, "primary"); 639 if (b == NULL) 640 return ENOMEM; 641 bs = sndbuf_create(c->name, "secondary"); 642 if (bs == NULL) { 643 sndbuf_destroy(b); 644 return ENOMEM; 645 } 646 sndbuf_setup(bs, NULL, 0); 647 c->bufhard = b; 648 c->bufsoft = bs; 649 c->flags = 0; 650 c->feederflags = 0; 651 c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir); 652 if (c->devinfo == NULL) { 653 sndbuf_destroy(bs); 654 sndbuf_destroy(b); 655 return ENODEV; 656 } 657 if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) { 658 sndbuf_destroy(bs); 659 sndbuf_destroy(b); 660 return ENOMEM; 661 } 662 chn_setdir(c, dir); 663 664 /* And the secondary bufhard. */ 665 sndbuf_setfmt(b, AFMT_U8); 666 sndbuf_setfmt(bs, AFMT_U8); 667 CHN_UNLOCK(c); 668 return 0; 669} 670 671int 672chn_kill(struct pcm_channel *c) 673{ 674 struct snd_dbuf *b = c->bufhard; 675 struct snd_dbuf *bs = c->bufsoft; 676 677 CHN_LOCK(c); 678 if (c->flags & CHN_F_TRIGGERED) 679 chn_trigger(c, PCMTRIG_ABORT); 680 while (chn_removefeeder(c) == 0); 681 if (CHANNEL_FREE(c->methods, c->devinfo)) 682 sndbuf_free(c->bufhard); 683 c->flags |= CHN_F_DEAD; 684 sndbuf_destroy(bs); 685 sndbuf_destroy(b); 686 chn_lockdestroy(c); 687 return 0; 688} 689 690int 691chn_setdir(struct pcm_channel *c, int dir) 692{ 693 struct snd_dbuf *b = c->bufhard; 694 int r; 695 696 CHN_LOCKASSERT(c); 697 c->direction = dir; 698 r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); 699 if (!r && ISA_DMA(b)) 700 sndbuf_isadmasetdir(b, c->direction); 701 return r; 702} 703 704int 705chn_setvolume(struct pcm_channel *c, int left, int right) 706{ 707 CHN_LOCKASSERT(c); 708 /* could add a feeder for volume changing if channel returns -1 */ 709 c->volume = (left << 8) | right; 710 return 0; 711} 712 713static int 714chn_tryspeed(struct pcm_channel *c, int speed) 715{ 716 struct pcm_feeder *f; 717 struct snd_dbuf *b = c->bufhard; 718 struct snd_dbuf *bs = c->bufsoft; 719 int r, delta; 720 721 CHN_LOCKASSERT(c); 722 DEB(printf("setspeed, channel %s\n", c->name)); 723 DEB(printf("want speed %d, ", speed)); 724 if (speed <= 0) 725 return EINVAL; 726 if (CANCHANGE(c)) { 727 r = 0; 728 c->speed = speed; 729 sndbuf_setspd(bs, speed); 730 RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); 731 DEB(printf("try speed %d, ", speed)); 732 sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed)); 733 DEB(printf("got speed %d\n", sndbuf_getspd(b))); 734 735 delta = sndbuf_getspd(b) - sndbuf_getspd(bs); 736 if (delta < 0) 737 delta = -delta; 738 739 c->feederflags &= ~(1 << FEEDER_RATE); 740 if (delta > 500) 741 c->feederflags |= 1 << FEEDER_RATE; 742 else 743 sndbuf_setspd(bs, sndbuf_getspd(b)); 744 745 r = chn_buildfeeder(c); 746 DEB(printf("r = %d\n", r)); 747 if (r) 748 goto out; 749 750 r = chn_setblocksize(c, 0, 0); 751 if (r) 752 goto out; 753 754 if (!(c->feederflags & (1 << FEEDER_RATE))) 755 goto out; 756 757 r = EINVAL; 758 f = chn_findfeeder(c, FEEDER_RATE); 759 DEB(printf("feedrate = %p\n", f)); 760 if (f == NULL) 761 goto out; 762 763 r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(bs)); 764 DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(bs), r)); 765 if (r) 766 goto out; 767 768 r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(b)); 769 DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(b), r)); 770out: 771 DEB(printf("setspeed done, r = %d\n", r)); 772 return r; 773 } else 774 return EINVAL; 775} 776 777int 778chn_setspeed(struct pcm_channel *c, int speed) 779{ 780 int r, oldspeed = c->speed; 781 782 r = chn_tryspeed(c, speed); 783 if (r) { 784 DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed)); 785 chn_tryspeed(c, oldspeed); 786 } 787 return r; 788} 789 790static int 791chn_tryformat(struct pcm_channel *c, u_int32_t fmt) 792{ 793 struct snd_dbuf *b = c->bufhard; 794 struct snd_dbuf *bs = c->bufsoft; 795 int r; 796 797 CHN_LOCKASSERT(c); 798 if (CANCHANGE(c)) { 799 DEB(printf("want format %d\n", fmt)); 800 c->format = fmt; 801 r = chn_buildfeeder(c); 802 if (r == 0) { 803 sndbuf_setfmt(b, c->feeder->desc->out); 804 sndbuf_setfmt(bs, fmt); 805 chn_resetbuf(c); 806 CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b)); 807 r = chn_tryspeed(c, c->speed); 808 } 809 return r; 810 } else 811 return EINVAL; 812} 813 814int 815chn_setformat(struct pcm_channel *c, u_int32_t fmt) 816{ 817 u_int32_t oldfmt = c->format; 818 int r; 819 820 r = chn_tryformat(c, fmt); 821 if (r) { 822 DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt)); 823 chn_tryformat(c, oldfmt); 824 } 825 return r; 826} 827 828int 829chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz) 830{ 831 struct snd_dbuf *b = c->bufhard; 832 struct snd_dbuf *bs = c->bufsoft; 833 int bufsz, irqhz, tmp, ret; 834 835 CHN_LOCKASSERT(c); 836 if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) 837 return EINVAL; 838 839 ret = 0; 840 DEB(printf("%s(%d, %d)\n", __FUNCTION__, blkcnt, blksz)); 841 if (blksz == 0 || blksz == -1) { 842 if (blksz == -1) 843 c->flags &= ~CHN_F_HAS_SIZE; 844 if (!(c->flags & CHN_F_HAS_SIZE)) { 845 blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / CHN_DEFAULT_HZ; 846 tmp = 32; 847 while (tmp <= blksz) 848 tmp <<= 1; 849 tmp >>= 1; 850 blksz = tmp; 851 blkcnt = CHN_2NDBUFMAXSIZE / blksz; 852 853 RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2); 854 RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz); 855 DEB(printf("%s: defaulting to (%d, %d)\n", __FUNCTION__, blkcnt, blksz)); 856 } else { 857 blkcnt = sndbuf_getblkcnt(bs); 858 blksz = sndbuf_getblksz(bs); 859 DEB(printf("%s: updating (%d, %d)\n", __FUNCTION__, blkcnt, blksz)); 860 } 861 } else { 862 ret = EINVAL; 863 if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE)) 864 goto out; 865 ret = 0; 866 c->flags |= CHN_F_HAS_SIZE; 867 } 868 869 bufsz = blkcnt * blksz; 870 871 ret = ENOMEM; 872 if (sndbuf_remalloc(bs, blkcnt, blksz)) 873 goto out; 874 ret = 0; 875 876 /* adjust for different hw format/speed */ 877 irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs); 878 DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __FUNCTION__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz)); 879 RANGE(irqhz, 16, 512); 880 881 sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz); 882 883 /* round down to 2^x */ 884 blksz = 32; 885 while (blksz <= sndbuf_getblksz(b)) 886 blksz <<= 1; 887 blksz >>= 1; 888 889 /* round down to fit hw bufhard size */ 890 RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2); 891 DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __FUNCTION__, blksz, sndbuf_getmaxsize(b))); 892 893 sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz)); 894 895 irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b); 896 DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz)); 897 898 chn_resetbuf(c); 899out: 900 return ret; 901} 902 903int 904chn_trigger(struct pcm_channel *c, int go) 905{ 906 struct snd_dbuf *b = c->bufhard; 907 int ret; 908 909 CHN_LOCKASSERT(c); 910 if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)) 911 sndbuf_isadmabounce(b); 912 ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go); 913 914 return ret; 915} 916 917int 918chn_getptr(struct pcm_channel *c) 919{ 920 int hwptr; 921 int a = (1 << c->align) - 1; 922 923 CHN_LOCKASSERT(c); 924 hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; 925 /* don't allow unaligned values in the hwa ptr */ 926#if 1 927 hwptr &= ~a ; /* Apply channel align mask */ 928#endif 929 hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ 930 return hwptr; 931} 932 933struct pcmchan_caps * 934chn_getcaps(struct pcm_channel *c) 935{ 936 CHN_LOCKASSERT(c); 937 return CHANNEL_GETCAPS(c->methods, c->devinfo); 938} 939 940u_int32_t 941chn_getformats(struct pcm_channel *c) 942{ 943 u_int32_t *fmtlist, fmts; 944 int i; 945 946 fmtlist = chn_getcaps(c)->fmtlist; 947 fmts = 0; 948 for (i = 0; fmtlist[i]; i++) 949 fmts |= fmtlist[i]; 950 951 return fmts; 952} 953 954static int 955chn_buildfeeder(struct pcm_channel *c) 956{ 957 struct feeder_class *fc; 958 struct pcm_feederdesc desc; 959 u_int32_t tmp[2], type, flags; 960 961 CHN_LOCKASSERT(c); 962 while (chn_removefeeder(c) == 0); 963 KASSERT((c->feeder == NULL), ("feeder chain not empty")); 964 965 c->align = sndbuf_getalign(c->bufsoft); 966 967 if (SLIST_EMPTY(&c->children)) { 968 fc = feeder_getclass(NULL); 969 if (fc == NULL) { 970 DEB(printf("can't find root feeder\n")); 971 return EINVAL; 972 } 973 if (chn_addfeeder(c, fc, NULL)) { 974 DEB(printf("can't add root feeder\n")); 975 return EINVAL; 976 } 977 c->feeder->desc->out = c->format; 978 } else { 979 desc.type = FEEDER_MIXER; 980 desc.in = 0; 981 desc.out = c->format; 982 desc.flags = 0; 983 fc = feeder_getclass(&desc); 984 if (fc == NULL) { 985 DEB(printf("can't find vchan feeder\n")); 986 return EINVAL; 987 } 988 if (chn_addfeeder(c, fc, &desc)) { 989 DEB(printf("can't add vchan feeder\n")); 990 return EINVAL; 991 } 992 } 993 flags = c->feederflags; 994 995 if ((c->flags & CHN_F_MAPPED) && (flags != 0)) { 996 DEB(printf("can't build feeder chain on mapped channel\n")); 997 return EINVAL; 998 } 999 DEB(printf("not mapped, flags %x\n", flags)); 1000 1001 for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) { 1002 if (flags & (1 << type)) { 1003 desc.type = type; 1004 desc.in = 0; 1005 desc.out = 0; 1006 desc.flags = 0; 1007 DEB(printf("find feeder type %d, ", type)); 1008 fc = feeder_getclass(&desc); 1009 DEB(printf("got %p\n", fc)); 1010 if (fc == NULL) { 1011 DEB(printf("can't find required feeder type %d\n", type)); 1012 return EINVAL; 1013 } 1014 1015 if (c->feeder->desc->out != fc->desc->in) { 1016 DEB(printf("build fmtchain from %x to %x: ", c->feeder->desc->out, fc->desc->in)); 1017 tmp[0] = fc->desc->in; 1018 tmp[1] = 0; 1019 if (chn_fmtchain(c, tmp) == 0) { 1020 DEB(printf("failed\n")); 1021 return EINVAL; 1022 } 1023 DEB(printf("ok\n")); 1024 } 1025 1026 if (chn_addfeeder(c, fc, fc->desc)) { 1027 DEB(printf("can't add feeder %p, output %x\n", fc, fc->desc->out)); 1028 return EINVAL; 1029 } 1030 DEB(printf("added feeder %p, output %x\n", fc, c->feeder->desc->out)); 1031 } 1032 } 1033 1034 if (!fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) { 1035 if (chn_fmtchain(c, chn_getcaps(c)->fmtlist) == 0) { 1036 DEB(printf("can't build fmtchain from %x\n", c->feeder->desc->out)); 1037 return EINVAL; 1038 } 1039 DEB(printf("built fmtchain from %x\n", c->feeder->desc->out)); 1040 } 1041 1042 return 0; 1043} 1044 1045int 1046chn_notify(struct pcm_channel *c, u_int32_t flags) 1047{ 1048 struct pcmchan_children *pce; 1049 struct pcm_channel *child; 1050 int run; 1051 1052 if (SLIST_EMPTY(&c->children)) 1053 return ENODEV; 1054 1055 run = (c->flags & CHN_F_TRIGGERED)? 1 : 0; 1056 /* 1057 * if the hwchan is running, we can't change its rate, format or 1058 * blocksize 1059 */ 1060 if (run) 1061 flags &= CHN_N_VOLUME | CHN_N_TRIGGER; 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 nrun; 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 nrun = 0; 1102 SLIST_FOREACH(pce, &c->children, link) { 1103 child = pce->channel; 1104 if (child->flags & CHN_F_TRIGGERED) 1105 nrun = 1; 1106 } 1107 if (nrun && !run) 1108 chn_start(c, 1); 1109 if (!nrun && run) 1110 chn_abort(c); 1111 } 1112 return 0; 1113} 1114