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