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