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