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