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