1 /* 2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 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 32 SND_DECLARE_FILE("$FreeBSD$"); 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 CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED)) 39 40 /* 41 #define DEB(x) x 42 */ 43 44 static int chn_targetirqrate = 32; 45 TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate); 46 47 static int 48 sysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS) 49 { 50 int err, val; 51 52 val = chn_targetirqrate; 53 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 54 if (val < 16 || val > 512) 55 err = EINVAL; 56 else 57 chn_targetirqrate = val; 58 59 return err; 60 } 61 SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW, 62 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", ""); 63 static int report_soft_formats = 1; 64 SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW, 65 &report_soft_formats, 1, "report software-emulated formats"); 66 67 static int chn_buildfeeder(struct pcm_channel *c); 68 69 static void 70 chn_lockinit(struct pcm_channel *c, int dir) 71 { 72 switch(dir) { 73 case PCMDIR_PLAY: 74 c->lock = snd_mtxcreate(c->name, "pcm play channel"); 75 break; 76 case PCMDIR_REC: 77 c->lock = snd_mtxcreate(c->name, "pcm record channel"); 78 break; 79 case PCMDIR_VIRTUAL: 80 c->lock = snd_mtxcreate(c->name, "pcm virtual play channel"); 81 break; 82 case 0: 83 c->lock = snd_mtxcreate(c->name, "pcm fake channel"); 84 break; 85 } 86 } 87 88 static void 89 chn_lockdestroy(struct pcm_channel *c) 90 { 91 snd_mtxfree(c->lock); 92 } 93 94 static int 95 chn_polltrigger(struct pcm_channel *c) 96 { 97 struct snd_dbuf *bs = c->bufsoft; 98 unsigned amt, lim; 99 100 CHN_LOCKASSERT(c); 101 if (c->flags & CHN_F_MAPPED) { 102 if (sndbuf_getprevblocks(bs) == 0) 103 return 1; 104 else 105 return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0; 106 } else { 107 amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); 108 lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1; 109 lim = 1; 110 return (amt >= lim)? 1 : 0; 111 } 112 return 0; 113 } 114 115 static int 116 chn_pollreset(struct pcm_channel *c) 117 { 118 struct snd_dbuf *bs = c->bufsoft; 119 120 CHN_LOCKASSERT(c); 121 sndbuf_updateprevtotal(bs); 122 return 1; 123 } 124 125 static void 126 chn_wakeup(struct pcm_channel *c) 127 { 128 struct snd_dbuf *bs = c->bufsoft; 129 struct pcmchan_children *pce; 130 131 CHN_LOCKASSERT(c); 132 if (SLIST_EMPTY(&c->children)) { 133 if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c)) 134 selwakeuppri(sndbuf_getsel(bs), PRIBIO); 135 } else { 136 SLIST_FOREACH(pce, &c->children, link) { 137 CHN_LOCK(pce->channel); 138 chn_wakeup(pce->channel); 139 CHN_UNLOCK(pce->channel); 140 } 141 } 142 143 wakeup(bs); 144 } 145 146 static int 147 chn_sleep(struct pcm_channel *c, char *str, int timeout) 148 { 149 struct snd_dbuf *bs = c->bufsoft; 150 int ret; 151 152 CHN_LOCKASSERT(c); 153 #ifdef USING_MUTEX 154 ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout); 155 #else 156 ret = tsleep(bs, PRIBIO | PCATCH, str, timeout); 157 #endif 158 159 return ret; 160 } 161 162 /* 163 * chn_dmaupdate() tracks the status of a dma transfer, 164 * updating pointers. It must be called at spltty(). 165 */ 166 167 static unsigned int 168 chn_dmaupdate(struct pcm_channel *c) 169 { 170 struct snd_dbuf *b = c->bufhard; 171 unsigned int delta, old, hwptr, amt; 172 173 KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0")); 174 CHN_LOCKASSERT(c); 175 176 old = sndbuf_gethwptr(b); 177 hwptr = chn_getptr(c); 178 delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b); 179 sndbuf_sethwptr(b, hwptr); 180 181 DEB( 182 if (delta >= ((sndbuf_getsize(b) * 15) / 16)) { 183 if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING))) 184 device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr); 185 } 186 ); 187 188 if (c->direction == PCMDIR_PLAY) { 189 amt = MIN(delta, sndbuf_getready(b)); 190 if (amt > 0) 191 sndbuf_dispose(b, NULL, amt); 192 } else { 193 amt = MIN(delta, sndbuf_getfree(b)); 194 if (amt > 0) 195 sndbuf_acquire(b, NULL, amt); 196 } 197 198 return delta; 199 } 200 201 void 202 chn_wrupdate(struct pcm_channel *c) 203 { 204 int ret; 205 206 CHN_LOCKASSERT(c); 207 KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); 208 209 if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED)) 210 return; 211 chn_dmaupdate(c); 212 ret = chn_wrfeed(c); 213 /* tell the driver we've updated the primary buffer */ 214 chn_trigger(c, PCMTRIG_EMLDMAWR); 215 DEB(if (ret) 216 printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);) 217 218 } 219 220 int 221 chn_wrfeed(struct pcm_channel *c) 222 { 223 struct snd_dbuf *b = c->bufhard; 224 struct snd_dbuf *bs = c->bufsoft; 225 unsigned int ret, amt; 226 227 CHN_LOCKASSERT(c); 228 /* DEB( 229 if (c->flags & CHN_F_CLOSING) { 230 sndbuf_dump(b, "b", 0x02); 231 sndbuf_dump(bs, "bs", 0x02); 232 }) */ 233 234 if (c->flags & CHN_F_MAPPED) 235 sndbuf_acquire(bs, NULL, sndbuf_getfree(bs)); 236 237 amt = sndbuf_getfree(b); 238 KASSERT(amt <= sndbuf_getsize(bs), 239 ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name, 240 amt, sndbuf_getsize(bs), c->flags)); 241 if (sndbuf_getready(bs) < amt) 242 c->xruns++; 243 244 ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC; 245 if (ret == 0 && sndbuf_getfree(b) < amt) 246 chn_wakeup(c); 247 248 return ret; 249 } 250 251 static void 252 chn_wrintr(struct pcm_channel *c) 253 { 254 int ret; 255 256 CHN_LOCKASSERT(c); 257 /* update pointers in primary buffer */ 258 chn_dmaupdate(c); 259 /* ...and feed from secondary to primary */ 260 ret = chn_wrfeed(c); 261 /* tell the driver we've updated the primary buffer */ 262 chn_trigger(c, PCMTRIG_EMLDMAWR); 263 DEB(if (ret) 264 printf("chn_wrintr: chn_wrfeed returned %d\n", ret);) 265 } 266 267 /* 268 * user write routine - uiomove data into secondary buffer, trigger if necessary 269 * if blocking, sleep, rinse and repeat. 270 * 271 * called externally, so must handle locking 272 */ 273 274 int 275 chn_write(struct pcm_channel *c, struct uio *buf) 276 { 277 int ret, timeout, newsize, count, sz; 278 struct snd_dbuf *bs = c->bufsoft; 279 void *off; 280 int t, x,togo,p; 281 282 CHN_LOCKASSERT(c); 283 /* 284 * XXX Certain applications attempt to write larger size 285 * of pcm data than c->blocksize2nd without blocking, 286 * resulting partial write. Expand the block size so that 287 * the write operation avoids blocking. 288 */ 289 if ((c->flags & CHN_F_NBIO) && buf->uio_resid > sndbuf_getblksz(bs)) { 290 DEB(device_printf(c->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n", 291 buf->uio_resid, sndbuf_getblksz(bs))); 292 newsize = 16; 293 while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2)) 294 newsize <<= 1; 295 chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize); 296 DEB(device_printf(c->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs))); 297 } 298 299 ret = 0; 300 count = hz; 301 while (!ret && (buf->uio_resid > 0) && (count > 0)) { 302 sz = sndbuf_getfree(bs); 303 if (sz == 0) { 304 if (c->flags & CHN_F_NBIO) 305 ret = EWOULDBLOCK; 306 else { 307 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); 308 if (timeout < 1) 309 timeout = 1; 310 timeout = 1; 311 ret = chn_sleep(c, "pcmwr", timeout); 312 if (ret == EWOULDBLOCK) { 313 count -= timeout; 314 ret = 0; 315 } else if (ret == 0) 316 count = hz; 317 } 318 } else { 319 sz = MIN(sz, buf->uio_resid); 320 KASSERT(sz > 0, ("confusion in chn_write")); 321 /* printf("sz: %d\n", sz); */ 322 323 /* 324 * The following assumes that the free space in 325 * the buffer can never be less around the 326 * unlock-uiomove-lock sequence. 327 */ 328 togo = sz; 329 while (ret == 0 && togo> 0) { 330 p = sndbuf_getfreeptr(bs); 331 t = MIN(togo, sndbuf_getsize(bs) - p); 332 off = sndbuf_getbufofs(bs, p); 333 CHN_UNLOCK(c); 334 ret = uiomove(off, t, buf); 335 CHN_LOCK(c); 336 togo -= t; 337 x = sndbuf_acquire(bs, NULL, t); 338 } 339 ret = 0; 340 if (ret == 0 && !(c->flags & CHN_F_TRIGGERED)) 341 chn_start(c, 0); 342 } 343 } 344 /* printf("ret: %d left: %d\n", ret, buf->uio_resid); */ 345 346 if (count <= 0) { 347 c->flags |= CHN_F_DEAD; 348 printf("%s: play interrupt timeout, channel dead\n", c->name); 349 } 350 351 return ret; 352 } 353 354 static int 355 chn_rddump(struct pcm_channel *c, unsigned int cnt) 356 { 357 struct snd_dbuf *b = c->bufhard; 358 359 CHN_LOCKASSERT(c); 360 sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt); 361 return sndbuf_dispose(b, NULL, cnt); 362 } 363 364 /* 365 * Feed new data from the read buffer. Can be called in the bottom half. 366 * Hence must be called at spltty. 367 */ 368 int 369 chn_rdfeed(struct pcm_channel *c) 370 { 371 struct snd_dbuf *b = c->bufhard; 372 struct snd_dbuf *bs = c->bufsoft; 373 unsigned int ret, amt; 374 375 CHN_LOCKASSERT(c); 376 DEB( 377 if (c->flags & CHN_F_CLOSING) { 378 sndbuf_dump(b, "b", 0x02); 379 sndbuf_dump(bs, "bs", 0x02); 380 }) 381 382 amt = sndbuf_getready(b); 383 if (sndbuf_getfree(bs) < amt) { 384 c->xruns++; 385 amt = sndbuf_getfree(bs); 386 } 387 ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0; 388 389 amt = sndbuf_getready(b); 390 if (amt > 0) 391 chn_rddump(c, amt); 392 393 chn_wakeup(c); 394 395 return ret; 396 } 397 398 void 399 chn_rdupdate(struct pcm_channel *c) 400 { 401 int ret; 402 403 CHN_LOCKASSERT(c); 404 KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel")); 405 406 if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED)) 407 return; 408 chn_trigger(c, PCMTRIG_EMLDMARD); 409 chn_dmaupdate(c); 410 ret = chn_rdfeed(c); 411 if (ret) 412 printf("chn_rdfeed: %d\n", ret); 413 414 } 415 416 /* read interrupt routine. Must be called with interrupts blocked. */ 417 static void 418 chn_rdintr(struct pcm_channel *c) 419 { 420 int ret; 421 422 CHN_LOCKASSERT(c); 423 /* tell the driver to update the primary buffer if non-dma */ 424 chn_trigger(c, PCMTRIG_EMLDMARD); 425 /* update pointers in primary buffer */ 426 chn_dmaupdate(c); 427 /* ...and feed from primary to secondary */ 428 ret = chn_rdfeed(c); 429 } 430 431 /* 432 * user read routine - trigger if necessary, uiomove data from secondary buffer 433 * if blocking, sleep, rinse and repeat. 434 * 435 * called externally, so must handle locking 436 */ 437 438 int 439 chn_read(struct pcm_channel *c, struct uio *buf) 440 { 441 int ret, timeout, sz, count; 442 struct snd_dbuf *bs = c->bufsoft; 443 void *off; 444 int t, x,togo,p; 445 446 CHN_LOCKASSERT(c); 447 if (!(c->flags & CHN_F_TRIGGERED)) 448 chn_start(c, 0); 449 450 ret = 0; 451 count = hz; 452 while (!ret && (buf->uio_resid > 0) && (count > 0)) { 453 sz = MIN(buf->uio_resid, sndbuf_getready(bs)); 454 455 if (sz > 0) { 456 /* 457 * The following assumes that the free space in 458 * the buffer can never be less around the 459 * unlock-uiomove-lock sequence. 460 */ 461 togo = sz; 462 while (ret == 0 && togo> 0) { 463 p = sndbuf_getreadyptr(bs); 464 t = MIN(togo, sndbuf_getsize(bs) - p); 465 off = sndbuf_getbufofs(bs, p); 466 CHN_UNLOCK(c); 467 ret = uiomove(off, t, buf); 468 CHN_LOCK(c); 469 togo -= t; 470 x = sndbuf_dispose(bs, NULL, t); 471 } 472 ret = 0; 473 } else { 474 if (c->flags & CHN_F_NBIO) { 475 ret = EWOULDBLOCK; 476 } else { 477 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); 478 if (timeout < 1) 479 timeout = 1; 480 ret = chn_sleep(c, "pcmrd", timeout); 481 if (ret == EWOULDBLOCK) { 482 count -= timeout; 483 ret = 0; 484 } else { 485 count = hz; 486 } 487 488 } 489 } 490 } 491 492 if (count <= 0) { 493 c->flags |= CHN_F_DEAD; 494 printf("%s: record interrupt timeout, channel dead\n", c->name); 495 } 496 497 return ret; 498 } 499 500 void 501 chn_intr(struct pcm_channel *c) 502 { 503 CHN_LOCK(c); 504 c->interrupts++; 505 if (c->direction == PCMDIR_PLAY) 506 chn_wrintr(c); 507 else 508 chn_rdintr(c); 509 CHN_UNLOCK(c); 510 } 511 512 u_int32_t 513 chn_start(struct pcm_channel *c, int force) 514 { 515 u_int32_t i, j; 516 struct snd_dbuf *b = c->bufhard; 517 struct snd_dbuf *bs = c->bufsoft; 518 519 CHN_LOCKASSERT(c); 520 /* if we're running, or if we're prevented from triggering, bail */ 521 if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force)) 522 return EINVAL; 523 524 i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs); 525 j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b); 526 if (force || (i >= j)) { 527 c->flags |= CHN_F_TRIGGERED; 528 /* 529 * if we're starting because a vchan started, don't feed any data 530 * or it becomes impossible to start vchans synchronised with the 531 * first one. the hardbuf should be empty so we top it up with 532 * silence to give it something to chew. the real data will be 533 * fed at the first irq. 534 */ 535 if (c->direction == PCMDIR_PLAY) { 536 if (SLIST_EMPTY(&c->children)) 537 chn_wrfeed(c); 538 else 539 sndbuf_fillsilence(b); 540 } 541 sndbuf_setrun(b, 1); 542 c->xruns = 0; 543 chn_trigger(c, PCMTRIG_START); 544 return 0; 545 } 546 547 return 0; 548 } 549 550 void 551 chn_resetbuf(struct pcm_channel *c) 552 { 553 struct snd_dbuf *b = c->bufhard; 554 struct snd_dbuf *bs = c->bufsoft; 555 556 c->blocks = 0; 557 sndbuf_reset(b); 558 sndbuf_reset(bs); 559 } 560 561 /* 562 * chn_sync waits until the space in the given channel goes above 563 * a threshold. The threshold is checked against fl or rl respectively. 564 * Assume that the condition can become true, do not check here... 565 */ 566 int 567 chn_sync(struct pcm_channel *c, int threshold) 568 { 569 u_long rdy; 570 int ret; 571 struct snd_dbuf *bs = c->bufsoft; 572 573 CHN_LOCKASSERT(c); 574 575 /* if we haven't yet started and nothing is buffered, else start*/ 576 if (!(c->flags & CHN_F_TRIGGERED)) { 577 if (sndbuf_getready(bs) > 0) { 578 ret = chn_start(c, 1); 579 if (ret) 580 return ret; 581 } else { 582 return 0; 583 } 584 } 585 586 for (;;) { 587 rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); 588 if (rdy <= threshold) { 589 ret = chn_sleep(c, "pcmsyn", 1); 590 if (ret == ERESTART || ret == EINTR) { 591 DEB(printf("chn_sync: tsleep returns %d\n", ret)); 592 return -1; 593 } 594 } else 595 break; 596 } 597 return 0; 598 } 599 600 /* called externally, handle locking */ 601 int 602 chn_poll(struct pcm_channel *c, int ev, struct thread *td) 603 { 604 struct snd_dbuf *bs = c->bufsoft; 605 int ret; 606 607 CHN_LOCKASSERT(c); 608 if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED)) 609 chn_start(c, 1); 610 ret = 0; 611 if (chn_polltrigger(c) && chn_pollreset(c)) 612 ret = ev; 613 else 614 selrecord(td, sndbuf_getsel(bs)); 615 return ret; 616 } 617 618 /* 619 * chn_abort terminates a running dma transfer. it may sleep up to 200ms. 620 * it returns the number of bytes that have not been transferred. 621 * 622 * called from: dsp_close, dsp_ioctl, with channel locked 623 */ 624 int 625 chn_abort(struct pcm_channel *c) 626 { 627 int missing = 0; 628 struct snd_dbuf *b = c->bufhard; 629 struct snd_dbuf *bs = c->bufsoft; 630 631 CHN_LOCKASSERT(c); 632 if (!(c->flags & CHN_F_TRIGGERED)) 633 return 0; 634 c->flags |= CHN_F_ABORTING; 635 636 c->flags &= ~CHN_F_TRIGGERED; 637 /* kill the channel */ 638 chn_trigger(c, PCMTRIG_ABORT); 639 sndbuf_setrun(b, 0); 640 if (!(c->flags & CHN_F_VIRTUAL)) 641 chn_dmaupdate(c); 642 missing = sndbuf_getready(bs) + sndbuf_getready(b); 643 644 c->flags &= ~CHN_F_ABORTING; 645 return missing; 646 } 647 648 /* 649 * this routine tries to flush the dma transfer. It is called 650 * on a close of a playback channel. 651 * first, if there is data in the buffer, but the dma has not yet 652 * begun, we need to start it. 653 * next, we wait for the play buffer to drain 654 * finally, we stop the dma. 655 * 656 * called from: dsp_close, not valid for record channels. 657 */ 658 659 int 660 chn_flush(struct pcm_channel *c) 661 { 662 int ret, count, resid, resid_p; 663 struct snd_dbuf *b = c->bufhard; 664 struct snd_dbuf *bs = c->bufsoft; 665 666 CHN_LOCKASSERT(c); 667 KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel")); 668 DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags)); 669 670 /* if we haven't yet started and nothing is buffered, else start*/ 671 if (!(c->flags & CHN_F_TRIGGERED)) { 672 if (sndbuf_getready(bs) > 0) { 673 ret = chn_start(c, 1); 674 if (ret) 675 return ret; 676 } else { 677 return 0; 678 } 679 } 680 681 c->flags |= CHN_F_CLOSING; 682 resid = sndbuf_getready(bs) + sndbuf_getready(b); 683 resid_p = resid; 684 count = 10; 685 ret = 0; 686 while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) { 687 /* still pending output data. */ 688 ret = chn_sleep(c, "pcmflu", hz / 10); 689 if (ret == EWOULDBLOCK) 690 ret = 0; 691 if (ret == 0) { 692 resid = sndbuf_getready(bs) + sndbuf_getready(b); 693 if (resid == resid_p) 694 count--; 695 if (resid > resid_p) 696 DEB(printf("chn_flush: buffer length increasind %d -> %d\n", resid_p, resid)); 697 resid_p = resid; 698 } 699 } 700 if (count == 0) 701 DEB(printf("chn_flush: timeout, hw %d, sw %d\n", 702 sndbuf_getready(b), sndbuf_getready(bs))); 703 704 c->flags &= ~CHN_F_TRIGGERED; 705 /* kill the channel */ 706 chn_trigger(c, PCMTRIG_ABORT); 707 sndbuf_setrun(b, 0); 708 709 c->flags &= ~CHN_F_CLOSING; 710 return 0; 711 } 712 713 int 714 fmtvalid(u_int32_t fmt, u_int32_t *fmtlist) 715 { 716 int i; 717 718 for (i = 0; fmtlist[i]; i++) 719 if (fmt == fmtlist[i]) 720 return 1; 721 return 0; 722 } 723 724 int 725 chn_reset(struct pcm_channel *c, u_int32_t fmt) 726 { 727 int hwspd, r; 728 729 CHN_LOCKASSERT(c); 730 c->flags &= CHN_F_RESET; 731 c->interrupts = 0; 732 c->xruns = 0; 733 734 r = CHANNEL_RESET(c->methods, c->devinfo); 735 if (fmt != 0) { 736 hwspd = DSP_DEFAULT_SPEED; 737 /* only do this on a record channel until feederbuilder works */ 738 if (c->direction == PCMDIR_REC) 739 RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); 740 c->speed = hwspd; 741 742 if (r == 0) 743 r = chn_setformat(c, fmt); 744 if (r == 0) 745 r = chn_setspeed(c, hwspd); 746 if (r == 0) 747 r = chn_setvolume(c, 100, 100); 748 } 749 if (r == 0) 750 r = chn_setblocksize(c, 0, 0); 751 if (r == 0) { 752 chn_resetbuf(c); 753 r = CHANNEL_RESETDONE(c->methods, c->devinfo); 754 } 755 return r; 756 } 757 758 int 759 chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction) 760 { 761 struct feeder_class *fc; 762 struct snd_dbuf *b, *bs; 763 int ret; 764 765 chn_lockinit(c, dir); 766 767 b = NULL; 768 bs = NULL; 769 c->devinfo = NULL; 770 c->feeder = NULL; 771 772 ret = ENOMEM; 773 b = sndbuf_create(c->dev, c->name, "primary", c); 774 if (b == NULL) 775 goto out; 776 bs = sndbuf_create(c->dev, c->name, "secondary", c); 777 if (bs == NULL) 778 goto out; 779 780 CHN_LOCK(c); 781 782 ret = EINVAL; 783 fc = feeder_getclass(NULL); 784 if (fc == NULL) 785 goto out; 786 if (chn_addfeeder(c, fc, NULL)) 787 goto out; 788 789 /* 790 * XXX - sndbuf_setup() & sndbuf_resize() expect to be called 791 * with the channel unlocked because they are also called 792 * from driver methods that don't know about locking 793 */ 794 CHN_UNLOCK(c); 795 sndbuf_setup(bs, NULL, 0); 796 CHN_LOCK(c); 797 c->bufhard = b; 798 c->bufsoft = bs; 799 c->flags = 0; 800 c->feederflags = 0; 801 802 ret = ENODEV; 803 CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */ 804 c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction); 805 CHN_LOCK(c); 806 if (c->devinfo == NULL) 807 goto out; 808 809 ret = ENOMEM; 810 if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) 811 goto out; 812 813 ret = chn_setdir(c, direction); 814 if (ret) 815 goto out; 816 817 ret = sndbuf_setfmt(b, AFMT_U8); 818 if (ret) 819 goto out; 820 821 ret = sndbuf_setfmt(bs, AFMT_U8); 822 if (ret) 823 goto out; 824 825 826 out: 827 CHN_UNLOCK(c); 828 if (ret) { 829 if (c->devinfo) { 830 if (CHANNEL_FREE(c->methods, c->devinfo)) 831 sndbuf_free(b); 832 } 833 if (bs) 834 sndbuf_destroy(bs); 835 if (b) 836 sndbuf_destroy(b); 837 c->flags |= CHN_F_DEAD; 838 chn_lockdestroy(c); 839 840 return ret; 841 } 842 843 return 0; 844 } 845 846 int 847 chn_kill(struct pcm_channel *c) 848 { 849 struct snd_dbuf *b = c->bufhard; 850 struct snd_dbuf *bs = c->bufsoft; 851 852 if (c->flags & CHN_F_TRIGGERED) 853 chn_trigger(c, PCMTRIG_ABORT); 854 while (chn_removefeeder(c) == 0); 855 if (CHANNEL_FREE(c->methods, c->devinfo)) 856 sndbuf_free(b); 857 c->flags |= CHN_F_DEAD; 858 sndbuf_destroy(bs); 859 sndbuf_destroy(b); 860 chn_lockdestroy(c); 861 return 0; 862 } 863 864 int 865 chn_setdir(struct pcm_channel *c, int dir) 866 { 867 struct snd_dbuf *b = c->bufhard; 868 int r; 869 870 CHN_LOCKASSERT(c); 871 c->direction = dir; 872 r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); 873 if (!r && SND_DMA(b)) 874 sndbuf_dmasetdir(b, c->direction); 875 return r; 876 } 877 878 int 879 chn_setvolume(struct pcm_channel *c, int left, int right) 880 { 881 CHN_LOCKASSERT(c); 882 /* should add a feeder for volume changing if channel returns -1 */ 883 c->volume = (left << 8) | right; 884 return 0; 885 } 886 887 static int 888 chn_tryspeed(struct pcm_channel *c, int speed) 889 { 890 struct pcm_feeder *f; 891 struct snd_dbuf *b = c->bufhard; 892 struct snd_dbuf *bs = c->bufsoft; 893 struct snd_dbuf *x; 894 int r, delta; 895 896 CHN_LOCKASSERT(c); 897 DEB(printf("setspeed, channel %s\n", c->name)); 898 DEB(printf("want speed %d, ", speed)); 899 if (speed <= 0) 900 return EINVAL; 901 if (CANCHANGE(c)) { 902 r = 0; 903 c->speed = speed; 904 sndbuf_setspd(bs, speed); 905 RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); 906 DEB(printf("try speed %d, ", speed)); 907 sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed)); 908 DEB(printf("got speed %d\n", sndbuf_getspd(b))); 909 910 delta = sndbuf_getspd(b) - sndbuf_getspd(bs); 911 if (delta < 0) 912 delta = -delta; 913 914 c->feederflags &= ~(1 << FEEDER_RATE); 915 if (delta > 500) 916 c->feederflags |= 1 << FEEDER_RATE; 917 else 918 sndbuf_setspd(bs, sndbuf_getspd(b)); 919 920 r = chn_buildfeeder(c); 921 DEB(printf("r = %d\n", r)); 922 if (r) 923 goto out; 924 925 r = chn_setblocksize(c, 0, 0); 926 if (r) 927 goto out; 928 929 if (!(c->feederflags & (1 << FEEDER_RATE))) 930 goto out; 931 932 r = EINVAL; 933 f = chn_findfeeder(c, FEEDER_RATE); 934 DEB(printf("feedrate = %p\n", f)); 935 if (f == NULL) 936 goto out; 937 938 x = (c->direction == PCMDIR_REC)? b : bs; 939 r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x)); 940 DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r)); 941 if (r) 942 goto out; 943 944 x = (c->direction == PCMDIR_REC)? bs : b; 945 r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x)); 946 DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r)); 947 out: 948 DEB(printf("setspeed done, r = %d\n", r)); 949 return r; 950 } else 951 return EINVAL; 952 } 953 954 int 955 chn_setspeed(struct pcm_channel *c, int speed) 956 { 957 int r, oldspeed = c->speed; 958 959 r = chn_tryspeed(c, speed); 960 if (r) { 961 DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed)); 962 r = chn_tryspeed(c, oldspeed); 963 } 964 return r; 965 } 966 967 static int 968 chn_tryformat(struct pcm_channel *c, u_int32_t fmt) 969 { 970 struct snd_dbuf *b = c->bufhard; 971 struct snd_dbuf *bs = c->bufsoft; 972 int r; 973 974 CHN_LOCKASSERT(c); 975 if (CANCHANGE(c)) { 976 DEB(printf("want format %d\n", fmt)); 977 c->format = fmt; 978 r = chn_buildfeeder(c); 979 if (r == 0) { 980 sndbuf_setfmt(bs, c->format); 981 chn_resetbuf(c); 982 r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b)); 983 if (r == 0) 984 r = chn_tryspeed(c, c->speed); 985 } 986 return r; 987 } else 988 return EINVAL; 989 } 990 991 int 992 chn_setformat(struct pcm_channel *c, u_int32_t fmt) 993 { 994 u_int32_t oldfmt = c->format; 995 int r; 996 997 r = chn_tryformat(c, fmt); 998 if (r) { 999 DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt)); 1000 chn_tryformat(c, oldfmt); 1001 } 1002 return r; 1003 } 1004 1005 int 1006 chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz) 1007 { 1008 struct snd_dbuf *b = c->bufhard; 1009 struct snd_dbuf *bs = c->bufsoft; 1010 int irqhz, tmp, ret, maxsize, reqblksz, tmpblksz; 1011 1012 CHN_LOCKASSERT(c); 1013 if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) { 1014 KASSERT(sndbuf_getsize(bs) == 0 || 1015 sndbuf_getsize(bs) >= sndbuf_getsize(b), 1016 ("%s(%s): bufsoft size %d < bufhard size %d", __func__, 1017 c->name, sndbuf_getsize(bs), sndbuf_getsize(b))); 1018 return EINVAL; 1019 } 1020 c->flags |= CHN_F_SETBLOCKSIZE; 1021 1022 ret = 0; 1023 DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz)); 1024 if (blksz == 0 || blksz == -1) { 1025 if (blksz == -1) 1026 c->flags &= ~CHN_F_HAS_SIZE; 1027 if (!(c->flags & CHN_F_HAS_SIZE)) { 1028 blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate; 1029 tmp = 32; 1030 while (tmp <= blksz) 1031 tmp <<= 1; 1032 tmp >>= 1; 1033 blksz = tmp; 1034 blkcnt = CHN_2NDBUFMAXSIZE / blksz; 1035 1036 RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2); 1037 RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz); 1038 DEB(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz)); 1039 } else { 1040 blkcnt = sndbuf_getblkcnt(bs); 1041 blksz = sndbuf_getblksz(bs); 1042 DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz)); 1043 } 1044 } else { 1045 ret = EINVAL; 1046 if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE)) 1047 goto out; 1048 ret = 0; 1049 c->flags |= CHN_F_HAS_SIZE; 1050 } 1051 1052 reqblksz = blksz; 1053 1054 /* adjust for different hw format/speed */ 1055 irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz; 1056 DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz)); 1057 RANGE(irqhz, 16, 512); 1058 1059 tmpblksz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz; 1060 1061 /* round down to 2^x */ 1062 blksz = 32; 1063 while (blksz <= tmpblksz) 1064 blksz <<= 1; 1065 blksz >>= 1; 1066 1067 /* round down to fit hw buffer size */ 1068 if (sndbuf_getmaxsize(b) > 0) 1069 RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2); 1070 else 1071 /* virtual channels don't appear to allocate bufhard */ 1072 RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2); 1073 DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b))); 1074 1075 /* Increase the size of bufsoft if before increasing bufhard. */ 1076 maxsize = sndbuf_getsize(b); 1077 if (sndbuf_getsize(bs) > maxsize) 1078 maxsize = sndbuf_getsize(bs); 1079 if (reqblksz * blkcnt > maxsize) 1080 maxsize = reqblksz * blkcnt; 1081 if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) { 1082 ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz); 1083 if (ret) 1084 goto out1; 1085 } 1086 1087 CHN_UNLOCK(c); 1088 sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz)); 1089 CHN_LOCK(c); 1090 1091 /* Decrease the size of bufsoft after decreasing bufhard. */ 1092 maxsize = sndbuf_getsize(b); 1093 if (reqblksz * blkcnt > maxsize) 1094 maxsize = reqblksz * blkcnt; 1095 if (maxsize > sndbuf_getsize(bs)) 1096 printf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n", 1097 c->name, sndbuf_getsize(bs), maxsize); 1098 if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) { 1099 ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz); 1100 if (ret) 1101 goto out1; 1102 } 1103 1104 irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b); 1105 DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz)); 1106 1107 chn_resetbuf(c); 1108 out1: 1109 KASSERT(sndbuf_getsize(bs) == 0 || 1110 sndbuf_getsize(bs) >= sndbuf_getsize(b), 1111 ("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d", 1112 __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz, 1113 blksz, maxsize, blkcnt)); 1114 out: 1115 c->flags &= ~CHN_F_SETBLOCKSIZE; 1116 return ret; 1117 } 1118 1119 int 1120 chn_trigger(struct pcm_channel *c, int go) 1121 { 1122 struct snd_dbuf *b = c->bufhard; 1123 int ret; 1124 1125 CHN_LOCKASSERT(c); 1126 if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)) 1127 sndbuf_dmabounce(b); 1128 ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go); 1129 1130 return ret; 1131 } 1132 1133 int 1134 chn_getptr(struct pcm_channel *c) 1135 { 1136 int hwptr; 1137 int a = (1 << c->align) - 1; 1138 1139 CHN_LOCKASSERT(c); 1140 hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; 1141 /* don't allow unaligned values in the hwa ptr */ 1142 #if 1 1143 hwptr &= ~a ; /* Apply channel align mask */ 1144 #endif 1145 hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ 1146 return hwptr; 1147 } 1148 1149 struct pcmchan_caps * 1150 chn_getcaps(struct pcm_channel *c) 1151 { 1152 CHN_LOCKASSERT(c); 1153 return CHANNEL_GETCAPS(c->methods, c->devinfo); 1154 } 1155 1156 u_int32_t 1157 chn_getformats(struct pcm_channel *c) 1158 { 1159 u_int32_t *fmtlist, fmts; 1160 int i; 1161 1162 fmtlist = chn_getcaps(c)->fmtlist; 1163 fmts = 0; 1164 for (i = 0; fmtlist[i]; i++) 1165 fmts |= fmtlist[i]; 1166 1167 /* report software-supported formats */ 1168 if (report_soft_formats) 1169 fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U16_LE|AFMT_U16_BE| 1170 AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8; 1171 1172 return fmts; 1173 } 1174 1175 static int 1176 chn_buildfeeder(struct pcm_channel *c) 1177 { 1178 struct feeder_class *fc; 1179 struct pcm_feederdesc desc; 1180 u_int32_t tmp[2], type, flags, hwfmt; 1181 int err; 1182 1183 CHN_LOCKASSERT(c); 1184 while (chn_removefeeder(c) == 0); 1185 KASSERT((c->feeder == NULL), ("feeder chain not empty")); 1186 1187 c->align = sndbuf_getalign(c->bufsoft); 1188 1189 if (SLIST_EMPTY(&c->children)) { 1190 fc = feeder_getclass(NULL); 1191 KASSERT(fc != NULL, ("can't find root feeder")); 1192 1193 err = chn_addfeeder(c, fc, NULL); 1194 if (err) { 1195 DEB(printf("can't add root feeder, err %d\n", err)); 1196 1197 return err; 1198 } 1199 c->feeder->desc->out = c->format; 1200 } else { 1201 desc.type = FEEDER_MIXER; 1202 desc.in = 0; 1203 desc.out = c->format; 1204 desc.flags = 0; 1205 fc = feeder_getclass(&desc); 1206 if (fc == NULL) { 1207 DEB(printf("can't find vchan feeder\n")); 1208 1209 return EOPNOTSUPP; 1210 } 1211 1212 err = chn_addfeeder(c, fc, &desc); 1213 if (err) { 1214 DEB(printf("can't add vchan feeder, err %d\n", err)); 1215 1216 return err; 1217 } 1218 } 1219 flags = c->feederflags; 1220 1221 DEB(printf("feederflags %x\n", flags)); 1222 1223 for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) { 1224 if (flags & (1 << type)) { 1225 desc.type = type; 1226 desc.in = 0; 1227 desc.out = 0; 1228 desc.flags = 0; 1229 DEB(printf("find feeder type %d, ", type)); 1230 fc = feeder_getclass(&desc); 1231 DEB(printf("got %p\n", fc)); 1232 if (fc == NULL) { 1233 DEB(printf("can't find required feeder type %d\n", type)); 1234 1235 return EOPNOTSUPP; 1236 } 1237 1238 if (c->feeder->desc->out != fc->desc->in) { 1239 DEB(printf("build fmtchain from 0x%x to 0x%x: ", c->feeder->desc->out, fc->desc->in)); 1240 tmp[0] = fc->desc->in; 1241 tmp[1] = 0; 1242 if (chn_fmtchain(c, tmp) == 0) { 1243 DEB(printf("failed\n")); 1244 1245 return ENODEV; 1246 } 1247 DEB(printf("ok\n")); 1248 } 1249 1250 err = chn_addfeeder(c, fc, fc->desc); 1251 if (err) { 1252 DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err)); 1253 1254 return err; 1255 } 1256 DEB(printf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out)); 1257 } 1258 } 1259 1260 if (fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) { 1261 hwfmt = c->feeder->desc->out; 1262 } else { 1263 if (c->direction == PCMDIR_REC) { 1264 tmp[0] = c->format; 1265 tmp[1] = 0; 1266 hwfmt = chn_fmtchain(c, tmp); 1267 } else { 1268 #if 0 1269 u_int32_t *x = chn_getcaps(c)->fmtlist; 1270 printf("acceptable formats for %s:\n", c->name); 1271 while (*x) { 1272 printf("[0x%8x] ", *x); 1273 x++; 1274 } 1275 #endif 1276 hwfmt = chn_fmtchain(c, chn_getcaps(c)->fmtlist); 1277 } 1278 } 1279 1280 if (hwfmt == 0) 1281 return ENODEV; 1282 1283 sndbuf_setfmt(c->bufhard, hwfmt); 1284 1285 return 0; 1286 } 1287 1288 int 1289 chn_notify(struct pcm_channel *c, u_int32_t flags) 1290 { 1291 struct pcmchan_children *pce; 1292 struct pcm_channel *child; 1293 int run; 1294 1295 CHN_LOCK(c); 1296 1297 if (SLIST_EMPTY(&c->children)) { 1298 CHN_UNLOCK(c); 1299 return ENODEV; 1300 } 1301 1302 run = (c->flags & CHN_F_TRIGGERED)? 1 : 0; 1303 /* 1304 * if the hwchan is running, we can't change its rate, format or 1305 * blocksize 1306 */ 1307 if (run) 1308 flags &= CHN_N_VOLUME | CHN_N_TRIGGER; 1309 1310 if (flags & CHN_N_RATE) { 1311 /* 1312 * we could do something here, like scan children and decide on 1313 * the most appropriate rate to mix at, but we don't for now 1314 */ 1315 } 1316 if (flags & CHN_N_FORMAT) { 1317 /* 1318 * we could do something here, like scan children and decide on 1319 * the most appropriate mixer feeder to use, but we don't for now 1320 */ 1321 } 1322 if (flags & CHN_N_VOLUME) { 1323 /* 1324 * we could do something here but we don't for now 1325 */ 1326 } 1327 if (flags & CHN_N_BLOCKSIZE) { 1328 int blksz; 1329 /* 1330 * scan the children, find the lowest blocksize and use that 1331 * for the hard blocksize 1332 */ 1333 blksz = sndbuf_getmaxsize(c->bufhard) / 2; 1334 SLIST_FOREACH(pce, &c->children, link) { 1335 child = pce->channel; 1336 CHN_LOCK(child); 1337 if (sndbuf_getblksz(child->bufhard) < blksz) 1338 blksz = sndbuf_getblksz(child->bufhard); 1339 CHN_UNLOCK(child); 1340 } 1341 chn_setblocksize(c, 2, blksz); 1342 } 1343 if (flags & CHN_N_TRIGGER) { 1344 int nrun; 1345 /* 1346 * scan the children, and figure out if any are running 1347 * if so, we need to be running, otherwise we need to be stopped 1348 * if we aren't in our target sstate, move to it 1349 */ 1350 nrun = 0; 1351 SLIST_FOREACH(pce, &c->children, link) { 1352 child = pce->channel; 1353 CHN_LOCK(child); 1354 if (child->flags & CHN_F_TRIGGERED) 1355 nrun = 1; 1356 CHN_UNLOCK(child); 1357 } 1358 if (nrun && !run) 1359 chn_start(c, 1); 1360 if (!nrun && run) 1361 chn_abort(c); 1362 } 1363 CHN_UNLOCK(c); 1364 return 0; 1365 } 1366 1367 void 1368 chn_lock(struct pcm_channel *c) 1369 { 1370 CHN_LOCK(c); 1371 } 1372 1373 void 1374 chn_unlock(struct pcm_channel *c) 1375 { 1376 CHN_UNLOCK(c); 1377 } 1378