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