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