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