channel.c revision 149953
10Sstevel@tonic-gate/*- 20Sstevel@tonic-gate * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 30Sstevel@tonic-gate * Portions Copyright by Luigi Rizzo - 1997-99 40Sstevel@tonic-gate * All rights reserved. 51544Seschrock * 61544Seschrock * Redistribution and use in source and binary forms, with or without 70Sstevel@tonic-gate * modification, are permitted provided that the following conditions 80Sstevel@tonic-gate * are met: 90Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 100Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 110Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 120Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 130Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 140Sstevel@tonic-gate * 150Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 160Sstevel@tonic-gate * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 170Sstevel@tonic-gate * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 180Sstevel@tonic-gate * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 190Sstevel@tonic-gate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 200Sstevel@tonic-gate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 216336Sbholler * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 220Sstevel@tonic-gate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 239053Sjonathan.chew@sun.com * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 240Sstevel@tonic-gate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 250Sstevel@tonic-gate * SUCH DAMAGE. 260Sstevel@tonic-gate */ 270Sstevel@tonic-gate 280Sstevel@tonic-gate#include "opt_isa.h" 290Sstevel@tonic-gate 300Sstevel@tonic-gate#include <dev/sound/pcm/sound.h> 310Sstevel@tonic-gate 320Sstevel@tonic-gate#include "feeder_if.h" 330Sstevel@tonic-gate 340Sstevel@tonic-gateSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/channel.c 149953 2005-09-10 18:10:31Z netchild $"); 350Sstevel@tonic-gate 360Sstevel@tonic-gate#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */ 370Sstevel@tonic-gate#define DMA_ALIGN_THRESHOLD 4 385648Ssetje#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1)) 390Sstevel@tonic-gate 400Sstevel@tonic-gate#define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED)) 415648Ssetje 420Sstevel@tonic-gate/* 430Sstevel@tonic-gate#define DEB(x) x 440Sstevel@tonic-gate*/ 450Sstevel@tonic-gate 460Sstevel@tonic-gatestatic int chn_targetirqrate = 32; 470Sstevel@tonic-gateTUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate); 486671Sjjc 496671Sjjcstatic int 506671Sjjcsysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS) 519053Sjonathan.chew@sun.com{ 529053Sjonathan.chew@sun.com int err, val; 5312004Sjiang.liu@intel.com 549053Sjonathan.chew@sun.com val = chn_targetirqrate; 556671Sjjc err = sysctl_handle_int(oidp, &val, sizeof(val), req); 566671Sjjc if (val < 16 || val > 512) 570Sstevel@tonic-gate err = EINVAL; 580Sstevel@tonic-gate else 590Sstevel@tonic-gate chn_targetirqrate = val; 600Sstevel@tonic-gate 610Sstevel@tonic-gate return err; 620Sstevel@tonic-gate} 630Sstevel@tonic-gateSYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW, 640Sstevel@tonic-gate 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", ""); 650Sstevel@tonic-gatestatic int report_soft_formats = 1; 660Sstevel@tonic-gateSYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW, 670Sstevel@tonic-gate &report_soft_formats, 1, "report software-emulated formats"); 680Sstevel@tonic-gate 690Sstevel@tonic-gatestatic int chn_buildfeeder(struct pcm_channel *c); 700Sstevel@tonic-gate 710Sstevel@tonic-gatestatic void 720Sstevel@tonic-gatechn_lockinit(struct pcm_channel *c, int dir) 730Sstevel@tonic-gate{ 740Sstevel@tonic-gate switch(dir) { 750Sstevel@tonic-gate case PCMDIR_PLAY: 760Sstevel@tonic-gate c->lock = snd_mtxcreate(c->name, "pcm play channel"); 770Sstevel@tonic-gate break; 780Sstevel@tonic-gate case PCMDIR_REC: 790Sstevel@tonic-gate c->lock = snd_mtxcreate(c->name, "pcm record channel"); 800Sstevel@tonic-gate break; 819940SVikram.Hegde@Sun.COM case PCMDIR_VIRTUAL: 820Sstevel@tonic-gate c->lock = snd_mtxcreate(c->name, "pcm virtual play channel"); 830Sstevel@tonic-gate break; 840Sstevel@tonic-gate case 0: 850Sstevel@tonic-gate c->lock = snd_mtxcreate(c->name, "pcm fake channel"); 860Sstevel@tonic-gate break; 870Sstevel@tonic-gate } 880Sstevel@tonic-gate} 890Sstevel@tonic-gate 900Sstevel@tonic-gatestatic void 910Sstevel@tonic-gatechn_lockdestroy(struct pcm_channel *c) 920Sstevel@tonic-gate{ 930Sstevel@tonic-gate snd_mtxfree(c->lock); 940Sstevel@tonic-gate} 950Sstevel@tonic-gate 960Sstevel@tonic-gatestatic int 970Sstevel@tonic-gatechn_polltrigger(struct pcm_channel *c) 980Sstevel@tonic-gate{ 990Sstevel@tonic-gate struct snd_dbuf *bs = c->bufsoft; 1000Sstevel@tonic-gate unsigned amt, lim; 1010Sstevel@tonic-gate 1020Sstevel@tonic-gate CHN_LOCKASSERT(c); 1030Sstevel@tonic-gate if (c->flags & CHN_F_MAPPED) { 1040Sstevel@tonic-gate if (sndbuf_getprevblocks(bs) == 0) 1050Sstevel@tonic-gate return 1; 1060Sstevel@tonic-gate else 1070Sstevel@tonic-gate return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0; 1080Sstevel@tonic-gate } else { 1090Sstevel@tonic-gate amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); 1100Sstevel@tonic-gate#if 0 1110Sstevel@tonic-gate lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1; 1120Sstevel@tonic-gate#endif 1130Sstevel@tonic-gate lim = 1; 1140Sstevel@tonic-gate return (amt >= lim)? 1 : 0; 1150Sstevel@tonic-gate } 1160Sstevel@tonic-gate return 0; 1170Sstevel@tonic-gate} 1180Sstevel@tonic-gate 1190Sstevel@tonic-gatestatic int 1200Sstevel@tonic-gatechn_pollreset(struct pcm_channel *c) 1210Sstevel@tonic-gate{ 1225648Ssetje struct snd_dbuf *bs = c->bufsoft; 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate CHN_LOCKASSERT(c); 1250Sstevel@tonic-gate sndbuf_updateprevtotal(bs); 1260Sstevel@tonic-gate return 1; 1275648Ssetje} 1280Sstevel@tonic-gate 1290Sstevel@tonic-gatestatic void 1300Sstevel@tonic-gatechn_wakeup(struct pcm_channel *c) 1310Sstevel@tonic-gate{ 1320Sstevel@tonic-gate struct snd_dbuf *bs = c->bufsoft; 1330Sstevel@tonic-gate struct pcmchan_children *pce; 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate CHN_LOCKASSERT(c); 1360Sstevel@tonic-gate if (SLIST_EMPTY(&c->children)) { 1370Sstevel@tonic-gate if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c)) 1385648Ssetje selwakeuppri(sndbuf_getsel(bs), PRIBIO); 1390Sstevel@tonic-gate } else { 1400Sstevel@tonic-gate SLIST_FOREACH(pce, &c->children, link) { 1410Sstevel@tonic-gate CHN_LOCK(pce->channel); 1420Sstevel@tonic-gate chn_wakeup(pce->channel); 1430Sstevel@tonic-gate CHN_UNLOCK(pce->channel); 1440Sstevel@tonic-gate } 1450Sstevel@tonic-gate } 1460Sstevel@tonic-gate 1470Sstevel@tonic-gate wakeup(bs); 1480Sstevel@tonic-gate} 1490Sstevel@tonic-gate 1500Sstevel@tonic-gatestatic int 1510Sstevel@tonic-gatechn_sleep(struct pcm_channel *c, char *str, int timeout) 1520Sstevel@tonic-gate{ 1530Sstevel@tonic-gate struct snd_dbuf *bs = c->bufsoft; 1540Sstevel@tonic-gate int ret; 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate CHN_LOCKASSERT(c); 1570Sstevel@tonic-gate#ifdef USING_MUTEX 1580Sstevel@tonic-gate ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout); 1590Sstevel@tonic-gate#else 1600Sstevel@tonic-gate ret = tsleep(bs, PRIBIO | PCATCH, str, timeout); 1610Sstevel@tonic-gate#endif 1620Sstevel@tonic-gate 1630Sstevel@tonic-gate return ret; 1640Sstevel@tonic-gate} 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate/* 1670Sstevel@tonic-gate * chn_dmaupdate() tracks the status of a dma transfer, 1680Sstevel@tonic-gate * updating pointers. It must be called at spltty(). 1690Sstevel@tonic-gate */ 1700Sstevel@tonic-gate 1710Sstevel@tonic-gatestatic unsigned int 1720Sstevel@tonic-gatechn_dmaupdate(struct pcm_channel *c) 1730Sstevel@tonic-gate{ 1740Sstevel@tonic-gate struct snd_dbuf *b = c->bufhard; 1750Sstevel@tonic-gate unsigned int delta, old, hwptr, amt; 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0")); 1780Sstevel@tonic-gate CHN_LOCKASSERT(c); 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate old = sndbuf_gethwptr(b); 1810Sstevel@tonic-gate hwptr = chn_getptr(c); 1820Sstevel@tonic-gate delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b); 1830Sstevel@tonic-gate sndbuf_sethwptr(b, hwptr); 1840Sstevel@tonic-gate 1850Sstevel@tonic-gate DEB( 1860Sstevel@tonic-gate if (delta >= ((sndbuf_getsize(b) * 15) / 16)) { 1870Sstevel@tonic-gate if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING))) 1880Sstevel@tonic-gate device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr); 1890Sstevel@tonic-gate } 1900Sstevel@tonic-gate ); 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate if (c->direction == PCMDIR_PLAY) { 1930Sstevel@tonic-gate amt = MIN(delta, sndbuf_getready(b)); 1940Sstevel@tonic-gate if (amt > 0) 1950Sstevel@tonic-gate sndbuf_dispose(b, NULL, amt); 1960Sstevel@tonic-gate } else { 1970Sstevel@tonic-gate amt = MIN(delta, sndbuf_getfree(b)); 1980Sstevel@tonic-gate if (amt > 0) 1998215SVikram.Hegde@Sun.COM sndbuf_acquire(b, NULL, amt); 2008215SVikram.Hegde@Sun.COM } 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate return delta; 2030Sstevel@tonic-gate} 2040Sstevel@tonic-gate 2050Sstevel@tonic-gatevoid 2060Sstevel@tonic-gatechn_wrupdate(struct pcm_channel *c) 2070Sstevel@tonic-gate{ 2080Sstevel@tonic-gate int ret; 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate CHN_LOCKASSERT(c); 2110Sstevel@tonic-gate KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); 2120Sstevel@tonic-gate 2130Sstevel@tonic-gate if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED)) 2140Sstevel@tonic-gate return; 2150Sstevel@tonic-gate chn_dmaupdate(c); 2160Sstevel@tonic-gate ret = chn_wrfeed(c); 2170Sstevel@tonic-gate /* tell the driver we've updated the primary buffer */ 2180Sstevel@tonic-gate chn_trigger(c, PCMTRIG_EMLDMAWR); 2190Sstevel@tonic-gate DEB(if (ret) 2200Sstevel@tonic-gate printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);) 2210Sstevel@tonic-gate 2227656SSherry.Moore@Sun.COM} 2233446Smrj 2240Sstevel@tonic-gateint 2250Sstevel@tonic-gatechn_wrfeed(struct pcm_channel *c) 2260Sstevel@tonic-gate{ 2275648Ssetje struct snd_dbuf *b = c->bufhard; 2280Sstevel@tonic-gate struct snd_dbuf *bs = c->bufsoft; 2290Sstevel@tonic-gate unsigned int ret, amt; 2303446Smrj 2313446Smrj CHN_LOCKASSERT(c); 2323446Smrj#if 0 2335648Ssetje DEB( 2343446Smrj if (c->flags & CHN_F_CLOSING) { 2356336Sbholler sndbuf_dump(b, "b", 0x02); 2363446Smrj sndbuf_dump(bs, "bs", 0x02); 2375648Ssetje }) 2386336Sbholler#endif 2396336Sbholler 2406336Sbholler if (c->flags & CHN_F_MAPPED) 2413446Smrj sndbuf_acquire(bs, NULL, sndbuf_getfree(bs)); 2423446Smrj 2439053Sjonathan.chew@sun.com amt = sndbuf_getfree(b); 2449053Sjonathan.chew@sun.com KASSERT(amt <= sndbuf_getsize(bs), 2453446Smrj ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name, 2463446Smrj amt, sndbuf_getsize(bs), c->flags)); 2473446Smrj 2483446Smrj if (SLIST_EMPTY(&c->children)) { 2493446Smrj /* 2503446Smrj * Hardware channel 2515648Ssetje */ 2525648Ssetje if (sndbuf_getready(bs) < amt) 2533446Smrj c->xruns++; 2540Sstevel@tonic-gate ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC; 2550Sstevel@tonic-gate } else { 2560Sstevel@tonic-gate /* 2570Sstevel@tonic-gate * vchan 2580Sstevel@tonic-gate */ 2590Sstevel@tonic-gate if (amt > 0) { 2600Sstevel@tonic-gate ret = sndbuf_feed(bs, b, c, c->feeder, amt); 261 /* 262 * Possible vchan xruns. There should be no empty space 263 * left in buffer. 264 */ 265 if (sndbuf_getfree(b) > 0) 266 c->xruns++; 267 } else 268 ret = ENOSPC; 269 } 270 271 if (ret == 0 && sndbuf_getfree(b) < amt) 272 chn_wakeup(c); 273 274 return ret; 275} 276 277static void 278chn_wrintr(struct pcm_channel *c) 279{ 280 int ret; 281 282 CHN_LOCKASSERT(c); 283 /* update pointers in primary buffer */ 284 chn_dmaupdate(c); 285 /* ...and feed from secondary to primary */ 286 ret = chn_wrfeed(c); 287 /* tell the driver we've updated the primary buffer */ 288 chn_trigger(c, PCMTRIG_EMLDMAWR); 289 DEB(if (ret) 290 printf("chn_wrintr: chn_wrfeed returned %d\n", ret);) 291} 292 293/* 294 * user write routine - uiomove data into secondary buffer, trigger if necessary 295 * if blocking, sleep, rinse and repeat. 296 * 297 * called externally, so must handle locking 298 */ 299 300int 301chn_write(struct pcm_channel *c, struct uio *buf) 302{ 303 int ret, timeout, newsize, count, sz; 304 struct snd_dbuf *bs = c->bufsoft; 305 void *off; 306 int t, x,togo,p; 307 308 CHN_LOCKASSERT(c); 309 /* 310 * XXX Certain applications attempt to write larger size 311 * of pcm data than c->blocksize2nd without blocking, 312 * resulting partial write. Expand the block size so that 313 * the write operation avoids blocking. 314 */ 315 if ((c->flags & CHN_F_NBIO) && buf->uio_resid > sndbuf_getblksz(bs)) { 316 DEB(device_printf(c->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n", 317 buf->uio_resid, sndbuf_getblksz(bs))); 318 newsize = 16; 319 while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2)) 320 newsize <<= 1; 321 chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize); 322 DEB(device_printf(c->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs))); 323 } 324 325 ret = 0; 326 count = hz; 327 while (!ret && (buf->uio_resid > 0) && (count > 0)) { 328 sz = sndbuf_getfree(bs); 329 if (sz == 0) { 330 if (c->flags & CHN_F_NBIO) 331 ret = EWOULDBLOCK; 332 else { 333 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); 334 if (timeout < 1) 335 timeout = 1; 336 timeout = 1; 337 ret = chn_sleep(c, "pcmwr", timeout); 338 if (ret == EWOULDBLOCK) { 339 count -= timeout; 340 ret = 0; 341 } else if (ret == 0) 342 count = hz; 343 } 344 } else { 345 sz = MIN(sz, buf->uio_resid); 346 KASSERT(sz > 0, ("confusion in chn_write")); 347 /* printf("sz: %d\n", sz); */ 348 349 /* 350 * The following assumes that the free space in 351 * the buffer can never be less around the 352 * unlock-uiomove-lock sequence. 353 */ 354 togo = sz; 355 while (ret == 0 && togo> 0) { 356 p = sndbuf_getfreeptr(bs); 357 t = MIN(togo, sndbuf_getsize(bs) - p); 358 off = sndbuf_getbufofs(bs, p); 359 CHN_UNLOCK(c); 360 ret = uiomove(off, t, buf); 361 CHN_LOCK(c); 362 togo -= t; 363 x = sndbuf_acquire(bs, NULL, t); 364 } 365 ret = 0; 366 if (ret == 0 && !(c->flags & CHN_F_TRIGGERED)) 367 chn_start(c, 0); 368 } 369 } 370 /* printf("ret: %d left: %d\n", ret, buf->uio_resid); */ 371 372 if (count <= 0) { 373 c->flags |= CHN_F_DEAD; 374 printf("%s: play interrupt timeout, channel dead\n", c->name); 375 } 376 377 return ret; 378} 379 380static int 381chn_rddump(struct pcm_channel *c, unsigned int cnt) 382{ 383 struct snd_dbuf *b = c->bufhard; 384 385 CHN_LOCKASSERT(c); 386#if 0 387 static uint32_t kk = 0; 388 printf("%u: dumping %d bytes\n", ++kk, cnt); 389#endif 390 c->xruns++; 391 sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt); 392 return sndbuf_dispose(b, NULL, cnt); 393} 394 395/* 396 * Feed new data from the read buffer. Can be called in the bottom half. 397 * Hence must be called at spltty. 398 */ 399int 400chn_rdfeed(struct pcm_channel *c) 401{ 402 struct snd_dbuf *b = c->bufhard; 403 struct snd_dbuf *bs = c->bufsoft; 404 unsigned int ret, amt; 405 406 CHN_LOCKASSERT(c); 407 DEB( 408 if (c->flags & CHN_F_CLOSING) { 409 sndbuf_dump(b, "b", 0x02); 410 sndbuf_dump(bs, "bs", 0x02); 411 }) 412 413#if 0 414 amt = sndbuf_getready(b); 415 if (sndbuf_getfree(bs) < amt) { 416 c->xruns++; 417 amt = sndbuf_getfree(bs); 418 } 419#endif 420 amt = sndbuf_getfree(bs); 421 if (amt < sndbuf_getready(b)) 422 c->xruns++; 423 ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0; 424 425 amt = sndbuf_getready(b); 426 if (amt > 0) 427 chn_rddump(c, amt); 428 429 chn_wakeup(c); 430 431 return ret; 432} 433 434void 435chn_rdupdate(struct pcm_channel *c) 436{ 437 int ret; 438 439 CHN_LOCKASSERT(c); 440 KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel")); 441 442 if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED)) 443 return; 444 chn_trigger(c, PCMTRIG_EMLDMARD); 445 chn_dmaupdate(c); 446 ret = chn_rdfeed(c); 447 if (ret) 448 printf("chn_rdfeed: %d\n", ret); 449 450} 451 452/* read interrupt routine. Must be called with interrupts blocked. */ 453static void 454chn_rdintr(struct pcm_channel *c) 455{ 456 int ret; 457 458 CHN_LOCKASSERT(c); 459 /* tell the driver to update the primary buffer if non-dma */ 460 chn_trigger(c, PCMTRIG_EMLDMARD); 461 /* update pointers in primary buffer */ 462 chn_dmaupdate(c); 463 /* ...and feed from primary to secondary */ 464 ret = chn_rdfeed(c); 465} 466 467/* 468 * user read routine - trigger if necessary, uiomove data from secondary buffer 469 * if blocking, sleep, rinse and repeat. 470 * 471 * called externally, so must handle locking 472 */ 473 474int 475chn_read(struct pcm_channel *c, struct uio *buf) 476{ 477 int ret, timeout, sz, count; 478 struct snd_dbuf *bs = c->bufsoft; 479 void *off; 480 int t, x,togo,p; 481 482 CHN_LOCKASSERT(c); 483 if (!(c->flags & CHN_F_TRIGGERED)) 484 chn_start(c, 0); 485 486 ret = 0; 487 count = hz; 488 while (!ret && (buf->uio_resid > 0) && (count > 0)) { 489 sz = MIN(buf->uio_resid, sndbuf_getready(bs)); 490 491 if (sz > 0) { 492 /* 493 * The following assumes that the free space in 494 * the buffer can never be less around the 495 * unlock-uiomove-lock sequence. 496 */ 497 togo = sz; 498 while (ret == 0 && togo> 0) { 499 p = sndbuf_getreadyptr(bs); 500 t = MIN(togo, sndbuf_getsize(bs) - p); 501 off = sndbuf_getbufofs(bs, p); 502 CHN_UNLOCK(c); 503 ret = uiomove(off, t, buf); 504 CHN_LOCK(c); 505 togo -= t; 506 x = sndbuf_dispose(bs, NULL, t); 507 } 508 ret = 0; 509 } else { 510 if (c->flags & CHN_F_NBIO) { 511 ret = EWOULDBLOCK; 512 } else { 513 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); 514 if (timeout < 1) 515 timeout = 1; 516 ret = chn_sleep(c, "pcmrd", timeout); 517 if (ret == EWOULDBLOCK) { 518 count -= timeout; 519 ret = 0; 520 } else { 521 count = hz; 522 } 523 524 } 525 } 526 } 527 528 if (count <= 0) { 529 c->flags |= CHN_F_DEAD; 530 printf("%s: record interrupt timeout, channel dead\n", c->name); 531 } 532 533 return ret; 534} 535 536void 537chn_intr(struct pcm_channel *c) 538{ 539 CHN_LOCK(c); 540 c->interrupts++; 541 if (c->direction == PCMDIR_PLAY) 542 chn_wrintr(c); 543 else 544 chn_rdintr(c); 545 CHN_UNLOCK(c); 546} 547 548u_int32_t 549chn_start(struct pcm_channel *c, int force) 550{ 551 u_int32_t i, j; 552 struct snd_dbuf *b = c->bufhard; 553 struct snd_dbuf *bs = c->bufsoft; 554 555 CHN_LOCKASSERT(c); 556 /* if we're running, or if we're prevented from triggering, bail */ 557 if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force)) 558 return EINVAL; 559 560 i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs); 561 j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b); 562 if (force || (i >= j)) { 563 c->flags |= CHN_F_TRIGGERED; 564 /* 565 * if we're starting because a vchan started, don't feed any data 566 * or it becomes impossible to start vchans synchronised with the 567 * first one. the hardbuf should be empty so we top it up with 568 * silence to give it something to chew. the real data will be 569 * fed at the first irq. 570 */ 571 if (c->direction == PCMDIR_PLAY) { 572 /* 573 * Reduce pops during playback startup. 574 */ 575 sndbuf_fillsilence(b); 576 if (SLIST_EMPTY(&c->children)) 577 chn_wrfeed(c); 578 } 579 sndbuf_setrun(b, 1); 580 c->xruns = 0; 581 chn_trigger(c, PCMTRIG_START); 582 return 0; 583 } 584 585 return 0; 586} 587 588void 589chn_resetbuf(struct pcm_channel *c) 590{ 591 struct snd_dbuf *b = c->bufhard; 592 struct snd_dbuf *bs = c->bufsoft; 593 594 c->blocks = 0; 595 sndbuf_reset(b); 596 sndbuf_reset(bs); 597} 598 599/* 600 * chn_sync waits until the space in the given channel goes above 601 * a threshold. The threshold is checked against fl or rl respectively. 602 * Assume that the condition can become true, do not check here... 603 */ 604int 605chn_sync(struct pcm_channel *c, int threshold) 606{ 607 u_long rdy; 608 int ret; 609 struct snd_dbuf *bs = c->bufsoft; 610 611 CHN_LOCKASSERT(c); 612 613 /* if we haven't yet started and nothing is buffered, else start*/ 614 if (!(c->flags & CHN_F_TRIGGERED)) { 615 if (sndbuf_getready(bs) > 0) { 616 ret = chn_start(c, 1); 617 if (ret) 618 return ret; 619 } else { 620 return 0; 621 } 622 } 623 624 for (;;) { 625 rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); 626 if (rdy <= threshold) { 627 ret = chn_sleep(c, "pcmsyn", 1); 628 if (ret == ERESTART || ret == EINTR) { 629 DEB(printf("chn_sync: tsleep returns %d\n", ret)); 630 return -1; 631 } 632 } else 633 break; 634 } 635 return 0; 636} 637 638/* called externally, handle locking */ 639int 640chn_poll(struct pcm_channel *c, int ev, struct thread *td) 641{ 642 struct snd_dbuf *bs = c->bufsoft; 643 int ret; 644 645 CHN_LOCKASSERT(c); 646 if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED)) 647 chn_start(c, 1); 648 ret = 0; 649 if (chn_polltrigger(c) && chn_pollreset(c)) 650 ret = ev; 651 else 652 selrecord(td, sndbuf_getsel(bs)); 653 return ret; 654} 655 656/* 657 * chn_abort terminates a running dma transfer. it may sleep up to 200ms. 658 * it returns the number of bytes that have not been transferred. 659 * 660 * called from: dsp_close, dsp_ioctl, with channel locked 661 */ 662int 663chn_abort(struct pcm_channel *c) 664{ 665 int missing = 0; 666 struct snd_dbuf *b = c->bufhard; 667 struct snd_dbuf *bs = c->bufsoft; 668 669 CHN_LOCKASSERT(c); 670 if (!(c->flags & CHN_F_TRIGGERED)) 671 return 0; 672 c->flags |= CHN_F_ABORTING; 673 674 c->flags &= ~CHN_F_TRIGGERED; 675 /* kill the channel */ 676 chn_trigger(c, PCMTRIG_ABORT); 677 sndbuf_setrun(b, 0); 678 if (!(c->flags & CHN_F_VIRTUAL)) 679 chn_dmaupdate(c); 680 missing = sndbuf_getready(bs) + sndbuf_getready(b); 681 682 c->flags &= ~CHN_F_ABORTING; 683 return missing; 684} 685 686/* 687 * this routine tries to flush the dma transfer. It is called 688 * on a close of a playback channel. 689 * first, if there is data in the buffer, but the dma has not yet 690 * begun, we need to start it. 691 * next, we wait for the play buffer to drain 692 * finally, we stop the dma. 693 * 694 * called from: dsp_close, not valid for record channels. 695 */ 696 697int 698chn_flush(struct pcm_channel *c) 699{ 700 int ret, count, resid, resid_p; 701 struct snd_dbuf *b = c->bufhard; 702 struct snd_dbuf *bs = c->bufsoft; 703 704 CHN_LOCKASSERT(c); 705 KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel")); 706 DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags)); 707 708 /* if we haven't yet started and nothing is buffered, else start*/ 709 if (!(c->flags & CHN_F_TRIGGERED)) { 710 if (sndbuf_getready(bs) > 0) { 711 ret = chn_start(c, 1); 712 if (ret) 713 return ret; 714 } else { 715 return 0; 716 } 717 } 718 719 c->flags |= CHN_F_CLOSING; 720 resid = sndbuf_getready(bs) + sndbuf_getready(b); 721 resid_p = resid; 722 count = 10; 723 ret = 0; 724 while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) { 725 /* still pending output data. */ 726 ret = chn_sleep(c, "pcmflu", hz / 10); 727 if (ret == EWOULDBLOCK) 728 ret = 0; 729 if (ret == 0) { 730 resid = sndbuf_getready(bs) + sndbuf_getready(b); 731 if (resid == resid_p) 732 count--; 733 if (resid > resid_p) 734 DEB(printf("chn_flush: buffer length increasind %d -> %d\n", resid_p, resid)); 735 resid_p = resid; 736 } 737 } 738 if (count == 0) 739 DEB(printf("chn_flush: timeout, hw %d, sw %d\n", 740 sndbuf_getready(b), sndbuf_getready(bs))); 741 742 c->flags &= ~CHN_F_TRIGGERED; 743 /* kill the channel */ 744 chn_trigger(c, PCMTRIG_ABORT); 745 sndbuf_setrun(b, 0); 746 747 c->flags &= ~CHN_F_CLOSING; 748 return 0; 749} 750 751int 752fmtvalid(u_int32_t fmt, u_int32_t *fmtlist) 753{ 754 int i; 755 756 for (i = 0; fmtlist[i]; i++) 757 if (fmt == fmtlist[i]) 758 return 1; 759 return 0; 760} 761 762int 763chn_reset(struct pcm_channel *c, u_int32_t fmt) 764{ 765 int hwspd, r; 766 767 CHN_LOCKASSERT(c); 768 c->flags &= CHN_F_RESET; 769 c->interrupts = 0; 770 c->xruns = 0; 771 772 r = CHANNEL_RESET(c->methods, c->devinfo); 773 if (fmt != 0) { 774#if 0 775 hwspd = DSP_DEFAULT_SPEED; 776 /* only do this on a record channel until feederbuilder works */ 777 if (c->direction == PCMDIR_REC) 778 RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); 779 c->speed = hwspd; 780#endif 781 hwspd = chn_getcaps(c)->minspeed; 782 c->speed = hwspd; 783 784 if (r == 0) 785 r = chn_setformat(c, fmt); 786 if (r == 0) 787 r = chn_setspeed(c, hwspd); 788 if (r == 0) 789 r = chn_setvolume(c, 100, 100); 790 } 791 if (r == 0) 792 r = chn_setblocksize(c, 0, 0); 793 if (r == 0) { 794 chn_resetbuf(c); 795 r = CHANNEL_RESETDONE(c->methods, c->devinfo); 796 } 797 return r; 798} 799 800int 801chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction) 802{ 803 struct feeder_class *fc; 804 struct snd_dbuf *b, *bs; 805 int ret; 806 807 chn_lockinit(c, dir); 808 809 b = NULL; 810 bs = NULL; 811 c->devinfo = NULL; 812 c->feeder = NULL; 813 814 ret = ENOMEM; 815 b = sndbuf_create(c->dev, c->name, "primary", c); 816 if (b == NULL) 817 goto out; 818 bs = sndbuf_create(c->dev, c->name, "secondary", c); 819 if (bs == NULL) 820 goto out; 821 822 CHN_LOCK(c); 823 824 ret = EINVAL; 825 fc = feeder_getclass(NULL); 826 if (fc == NULL) 827 goto out; 828 if (chn_addfeeder(c, fc, NULL)) 829 goto out; 830 831 /* 832 * XXX - sndbuf_setup() & sndbuf_resize() expect to be called 833 * with the channel unlocked because they are also called 834 * from driver methods that don't know about locking 835 */ 836 CHN_UNLOCK(c); 837 sndbuf_setup(bs, NULL, 0); 838 CHN_LOCK(c); 839 c->bufhard = b; 840 c->bufsoft = bs; 841 c->flags = 0; 842 c->feederflags = 0; 843 844 ret = ENODEV; 845 CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */ 846 c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction); 847 CHN_LOCK(c); 848 if (c->devinfo == NULL) 849 goto out; 850 851 ret = ENOMEM; 852 if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) 853 goto out; 854 855 ret = chn_setdir(c, direction); 856 if (ret) 857 goto out; 858 859 ret = sndbuf_setfmt(b, AFMT_U8); 860 if (ret) 861 goto out; 862 863 ret = sndbuf_setfmt(bs, AFMT_U8); 864 if (ret) 865 goto out; 866 867 868out: 869 CHN_UNLOCK(c); 870 if (ret) { 871 if (c->devinfo) { 872 if (CHANNEL_FREE(c->methods, c->devinfo)) 873 sndbuf_free(b); 874 } 875 if (bs) 876 sndbuf_destroy(bs); 877 if (b) 878 sndbuf_destroy(b); 879 c->flags |= CHN_F_DEAD; 880 chn_lockdestroy(c); 881 882 return ret; 883 } 884 885 return 0; 886} 887 888int 889chn_kill(struct pcm_channel *c) 890{ 891 struct snd_dbuf *b = c->bufhard; 892 struct snd_dbuf *bs = c->bufsoft; 893 894 if (c->flags & CHN_F_TRIGGERED) 895 chn_trigger(c, PCMTRIG_ABORT); 896 while (chn_removefeeder(c) == 0); 897 if (CHANNEL_FREE(c->methods, c->devinfo)) 898 sndbuf_free(b); 899 c->flags |= CHN_F_DEAD; 900 sndbuf_destroy(bs); 901 sndbuf_destroy(b); 902 chn_lockdestroy(c); 903 return 0; 904} 905 906int 907chn_setdir(struct pcm_channel *c, int dir) 908{ 909#ifdef DEV_ISA 910 struct snd_dbuf *b = c->bufhard; 911#endif 912 int r; 913 914 CHN_LOCKASSERT(c); 915 c->direction = dir; 916 r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); 917#ifdef DEV_ISA 918 if (!r && SND_DMA(b)) 919 sndbuf_dmasetdir(b, c->direction); 920#endif 921 return r; 922} 923 924int 925chn_setvolume(struct pcm_channel *c, int left, int right) 926{ 927 CHN_LOCKASSERT(c); 928 /* should add a feeder for volume changing if channel returns -1 */ 929 c->volume = (left << 8) | right; 930 return 0; 931} 932 933static int 934chn_tryspeed(struct pcm_channel *c, int speed) 935{ 936 struct pcm_feeder *f; 937 struct snd_dbuf *b = c->bufhard; 938 struct snd_dbuf *bs = c->bufsoft; 939 struct snd_dbuf *x; 940 int r, delta; 941 942 CHN_LOCKASSERT(c); 943 DEB(printf("setspeed, channel %s\n", c->name)); 944 DEB(printf("want speed %d, ", speed)); 945 if (speed <= 0) 946 return EINVAL; 947 if (CANCHANGE(c)) { 948 r = 0; 949 c->speed = speed; 950 sndbuf_setspd(bs, speed); 951 RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); 952 DEB(printf("try speed %d, ", speed)); 953 sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed)); 954 DEB(printf("got speed %d\n", sndbuf_getspd(b))); 955 956 delta = sndbuf_getspd(b) - sndbuf_getspd(bs); 957 if (delta < 0) 958 delta = -delta; 959 960 c->feederflags &= ~(1 << FEEDER_RATE); 961 /* 962 * Used to be 500. It was too big! 963 */ 964 if (delta > 25) 965 c->feederflags |= 1 << FEEDER_RATE; 966 else 967 sndbuf_setspd(bs, sndbuf_getspd(b)); 968 969 r = chn_buildfeeder(c); 970 DEB(printf("r = %d\n", r)); 971 if (r) 972 goto out; 973 974 r = chn_setblocksize(c, 0, 0); 975 if (r) 976 goto out; 977 978 if (!(c->feederflags & (1 << FEEDER_RATE))) 979 goto out; 980 981 r = EINVAL; 982 f = chn_findfeeder(c, FEEDER_RATE); 983 DEB(printf("feedrate = %p\n", f)); 984 if (f == NULL) 985 goto out; 986 987 x = (c->direction == PCMDIR_REC)? b : bs; 988 r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x)); 989 DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r)); 990 if (r) 991 goto out; 992 993 x = (c->direction == PCMDIR_REC)? bs : b; 994 r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x)); 995 DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r)); 996out: 997 if (!r) 998 r = CHANNEL_SETFORMAT(c->methods, c->devinfo, 999 sndbuf_getfmt(b)); 1000 if (!r) 1001 sndbuf_setfmt(bs, c->format); 1002 DEB(printf("setspeed done, r = %d\n", r)); 1003 return r; 1004 } else 1005 return EINVAL; 1006} 1007 1008int 1009chn_setspeed(struct pcm_channel *c, int speed) 1010{ 1011 int r, oldspeed = c->speed; 1012 1013 r = chn_tryspeed(c, speed); 1014 if (r) { 1015 DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed)); 1016 r = chn_tryspeed(c, oldspeed); 1017 } 1018 return r; 1019} 1020 1021static int 1022chn_tryformat(struct pcm_channel *c, u_int32_t fmt) 1023{ 1024 struct snd_dbuf *b = c->bufhard; 1025 struct snd_dbuf *bs = c->bufsoft; 1026 int r; 1027 1028 CHN_LOCKASSERT(c); 1029 if (CANCHANGE(c)) { 1030 DEB(printf("want format %d\n", fmt)); 1031 c->format = fmt; 1032 r = chn_buildfeeder(c); 1033 if (r == 0) { 1034 sndbuf_setfmt(bs, c->format); 1035 chn_resetbuf(c); 1036 r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b)); 1037 if (r == 0) 1038 r = chn_tryspeed(c, c->speed); 1039 } 1040 return r; 1041 } else 1042 return EINVAL; 1043} 1044 1045int 1046chn_setformat(struct pcm_channel *c, u_int32_t fmt) 1047{ 1048 u_int32_t oldfmt = c->format; 1049 int r; 1050 1051 r = chn_tryformat(c, fmt); 1052 if (r) { 1053 DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt)); 1054 chn_tryformat(c, oldfmt); 1055 } 1056 return r; 1057} 1058 1059int 1060chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz) 1061{ 1062 struct snd_dbuf *b = c->bufhard; 1063 struct snd_dbuf *bs = c->bufsoft; 1064 int irqhz, tmp, ret, maxsize, reqblksz, tmpblksz; 1065 1066 CHN_LOCKASSERT(c); 1067 if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) { 1068 KASSERT(sndbuf_getsize(bs) == 0 || 1069 sndbuf_getsize(bs) >= sndbuf_getsize(b), 1070 ("%s(%s): bufsoft size %d < bufhard size %d", __func__, 1071 c->name, sndbuf_getsize(bs), sndbuf_getsize(b))); 1072 return EINVAL; 1073 } 1074 c->flags |= CHN_F_SETBLOCKSIZE; 1075 1076 ret = 0; 1077 DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz)); 1078 if (blksz == 0 || blksz == -1) { 1079 if (blksz == -1) 1080 c->flags &= ~CHN_F_HAS_SIZE; 1081 if (!(c->flags & CHN_F_HAS_SIZE)) { 1082 blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate; 1083 tmp = 32; 1084 while (tmp <= blksz) 1085 tmp <<= 1; 1086 tmp >>= 1; 1087 blksz = tmp; 1088 blkcnt = CHN_2NDBUFMAXSIZE / blksz; 1089 1090 RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2); 1091 RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz); 1092 DEB(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz)); 1093 } else { 1094 blkcnt = sndbuf_getblkcnt(bs); 1095 blksz = sndbuf_getblksz(bs); 1096 DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz)); 1097 } 1098 } else { 1099 ret = EINVAL; 1100 if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE)) 1101 goto out; 1102 ret = 0; 1103 c->flags |= CHN_F_HAS_SIZE; 1104 } 1105 1106 reqblksz = blksz; 1107 if (reqblksz < sndbuf_getbps(bs)) 1108 reqblksz = sndbuf_getbps(bs); 1109 if (reqblksz % sndbuf_getbps(bs)) 1110 reqblksz -= reqblksz % sndbuf_getbps(bs); 1111 1112 /* adjust for different hw format/speed */ 1113 irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz; 1114 DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz)); 1115 RANGE(irqhz, 16, 512); 1116 1117 tmpblksz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz; 1118 1119 /* round down to 2^x */ 1120 blksz = 32; 1121 while (blksz <= tmpblksz) 1122 blksz <<= 1; 1123 blksz >>= 1; 1124 1125 /* round down to fit hw buffer size */ 1126 if (sndbuf_getmaxsize(b) > 0) 1127 RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2); 1128 else 1129 /* virtual channels don't appear to allocate bufhard */ 1130 RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2); 1131 DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b))); 1132 1133 /* Increase the size of bufsoft if before increasing bufhard. */ 1134 maxsize = sndbuf_getsize(b); 1135 if (sndbuf_getsize(bs) > maxsize) 1136 maxsize = sndbuf_getsize(bs); 1137 if (reqblksz * blkcnt > maxsize) 1138 maxsize = reqblksz * blkcnt; 1139 if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) { 1140 ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz); 1141 if (ret) 1142 goto out1; 1143 } 1144 1145 CHN_UNLOCK(c); 1146 sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz)); 1147 CHN_LOCK(c); 1148 1149 /* Decrease the size of bufsoft after decreasing bufhard. */ 1150 maxsize = sndbuf_getsize(b); 1151 if (reqblksz * blkcnt > maxsize) 1152 maxsize = reqblksz * blkcnt; 1153 if (maxsize > sndbuf_getsize(bs)) 1154 printf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n", 1155 c->name, sndbuf_getsize(bs), maxsize); 1156 if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) { 1157 ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz); 1158 if (ret) 1159 goto out1; 1160 } 1161 1162 irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b); 1163 DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz)); 1164 1165 chn_resetbuf(c); 1166out1: 1167 KASSERT(sndbuf_getsize(bs) == 0 || 1168 sndbuf_getsize(bs) >= sndbuf_getsize(b), 1169 ("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d", 1170 __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz, 1171 blksz, maxsize, blkcnt)); 1172out: 1173 c->flags &= ~CHN_F_SETBLOCKSIZE; 1174#if 0 1175 if (1) { 1176 static uint32_t kk = 0; 1177 printf("%u: b %d/%d/%d : (%d)%d/0x%0x | bs %d/%d/%d : (%d)%d/0x%0x\n", ++kk, 1178 sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b), 1179 sndbuf_getbps(b), 1180 sndbuf_getspd(b), sndbuf_getfmt(b), 1181 sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs), 1182 sndbuf_getbps(bs), 1183 sndbuf_getspd(bs), sndbuf_getfmt(bs)); 1184 if (sndbuf_getsize(b) % sndbuf_getbps(b) || 1185 sndbuf_getblksz(b) % sndbuf_getbps(b) || 1186 sndbuf_getsize(bs) % sndbuf_getbps(bs) || 1187 sndbuf_getblksz(b) % sndbuf_getbps(b)) { 1188 printf("%u: bps/blksz alignment screwed!\n", kk); 1189 } 1190 } 1191#endif 1192 return ret; 1193} 1194 1195int 1196chn_trigger(struct pcm_channel *c, int go) 1197{ 1198#ifdef DEV_ISA 1199 struct snd_dbuf *b = c->bufhard; 1200#endif 1201 int ret; 1202 1203 CHN_LOCKASSERT(c); 1204#ifdef DEV_ISA 1205 if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)) 1206 sndbuf_dmabounce(b); 1207#endif 1208 ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go); 1209 1210 return ret; 1211} 1212 1213int 1214chn_getptr(struct pcm_channel *c) 1215{ 1216 int hwptr; 1217 int a = (1 << c->align) - 1; 1218 1219 CHN_LOCKASSERT(c); 1220 hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; 1221 /* don't allow unaligned values in the hwa ptr */ 1222#if 1 1223 hwptr &= ~a ; /* Apply channel align mask */ 1224#endif 1225 hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ 1226 return hwptr; 1227} 1228 1229struct pcmchan_caps * 1230chn_getcaps(struct pcm_channel *c) 1231{ 1232 CHN_LOCKASSERT(c); 1233 return CHANNEL_GETCAPS(c->methods, c->devinfo); 1234} 1235 1236u_int32_t 1237chn_getformats(struct pcm_channel *c) 1238{ 1239 u_int32_t *fmtlist, fmts; 1240 int i; 1241 1242 fmtlist = chn_getcaps(c)->fmtlist; 1243 fmts = 0; 1244 for (i = 0; fmtlist[i]; i++) 1245 fmts |= fmtlist[i]; 1246 1247 /* report software-supported formats */ 1248 if (report_soft_formats) 1249 fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE| 1250 AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE| 1251 AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE| 1252 AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8; 1253 1254 return fmts; 1255} 1256 1257static int 1258chn_buildfeeder(struct pcm_channel *c) 1259{ 1260 struct feeder_class *fc; 1261 struct pcm_feederdesc desc; 1262 u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; 1263 int err; 1264 1265 CHN_LOCKASSERT(c); 1266 while (chn_removefeeder(c) == 0); 1267 KASSERT((c->feeder == NULL), ("feeder chain not empty")); 1268 1269 c->align = sndbuf_getalign(c->bufsoft); 1270 1271 if (SLIST_EMPTY(&c->children)) { 1272 fc = feeder_getclass(NULL); 1273 KASSERT(fc != NULL, ("can't find root feeder")); 1274 1275 err = chn_addfeeder(c, fc, NULL); 1276 if (err) { 1277 DEB(printf("can't add root feeder, err %d\n", err)); 1278 1279 return err; 1280 } 1281 c->feeder->desc->out = c->format; 1282 } else { 1283 if (c->flags & CHN_F_HAS_VCHAN) { 1284 desc.type = FEEDER_MIXER; 1285 desc.in = 0; 1286 } else { 1287 DEB(printf("can't decide which feeder type to use!\n")); 1288 return EOPNOTSUPP; 1289 } 1290 desc.out = c->format; 1291 desc.flags = 0; 1292 fc = feeder_getclass(&desc); 1293 if (fc == NULL) { 1294 DEB(printf("can't find vchan feeder\n")); 1295 1296 return EOPNOTSUPP; 1297 } 1298 1299 err = chn_addfeeder(c, fc, &desc); 1300 if (err) { 1301 DEB(printf("can't add vchan feeder, err %d\n", err)); 1302 1303 return err; 1304 } 1305 } 1306 flags = c->feederflags; 1307 fmtlist = chn_getcaps(c)->fmtlist; 1308 1309 DEB(printf("feederflags %x\n", flags)); 1310 1311 for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) { 1312 if (flags & (1 << type)) { 1313 desc.type = type; 1314 desc.in = 0; 1315 desc.out = 0; 1316 desc.flags = 0; 1317 DEB(printf("find feeder type %d, ", type)); 1318 fc = feeder_getclass(&desc); 1319 DEB(printf("got %p\n", fc)); 1320 if (fc == NULL) { 1321 DEB(printf("can't find required feeder type %d\n", type)); 1322 1323 return EOPNOTSUPP; 1324 } 1325 1326 if ((type == FEEDER_RATE && 1327 !fmtvalid(fc->desc->in, fmtlist)) 1328 || c->feeder->desc->out != fc->desc->in) { 1329 DEB(printf("build fmtchain from 0x%x to 0x%x: ", c->feeder->desc->out, fc->desc->in)); 1330 tmp[0] = fc->desc->in; 1331 tmp[1] = 0; 1332 if (chn_fmtchain(c, tmp) == 0) { 1333 DEB(printf("failed\n")); 1334 1335 return ENODEV; 1336 } 1337 DEB(printf("ok\n")); 1338 } 1339 1340 err = chn_addfeeder(c, fc, fc->desc); 1341 if (err) { 1342 DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err)); 1343 1344 return err; 1345 } 1346 DEB(printf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out)); 1347 } 1348 } 1349 1350 if (fmtvalid(c->feeder->desc->out, fmtlist) 1351 && !(c->direction == PCMDIR_REC && 1352 c->format != c->feeder->desc->out)) 1353 hwfmt = c->feeder->desc->out; 1354 else { 1355 if (c->direction == PCMDIR_REC) { 1356 tmp[0] = c->format; 1357 tmp[1] = 0; 1358 hwfmt = chn_fmtchain(c, tmp); 1359 } else 1360 hwfmt = chn_fmtchain(c, fmtlist); 1361 } 1362 1363 if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) { 1364 DEB(printf("Invalid hardware format: 0x%x\n", hwfmt)); 1365 return ENODEV; 1366 } 1367 1368 sndbuf_setfmt(c->bufhard, hwfmt); 1369 1370 return 0; 1371} 1372 1373int 1374chn_notify(struct pcm_channel *c, u_int32_t flags) 1375{ 1376 struct pcmchan_children *pce; 1377 struct pcm_channel *child; 1378 int run; 1379 1380 CHN_LOCK(c); 1381 1382 if (SLIST_EMPTY(&c->children)) { 1383 CHN_UNLOCK(c); 1384 return ENODEV; 1385 } 1386 1387 run = (c->flags & CHN_F_TRIGGERED)? 1 : 0; 1388 /* 1389 * if the hwchan is running, we can't change its rate, format or 1390 * blocksize 1391 */ 1392 if (run) 1393 flags &= CHN_N_VOLUME | CHN_N_TRIGGER; 1394 1395 if (flags & CHN_N_RATE) { 1396 /* 1397 * we could do something here, like scan children and decide on 1398 * the most appropriate rate to mix at, but we don't for now 1399 */ 1400 } 1401 if (flags & CHN_N_FORMAT) { 1402 /* 1403 * we could do something here, like scan children and decide on 1404 * the most appropriate mixer feeder to use, but we don't for now 1405 */ 1406 } 1407 if (flags & CHN_N_VOLUME) { 1408 /* 1409 * we could do something here but we don't for now 1410 */ 1411 } 1412 if (flags & CHN_N_BLOCKSIZE) { 1413 int blksz; 1414 /* 1415 * scan the children, find the lowest blocksize and use that 1416 * for the hard blocksize 1417 */ 1418 blksz = sndbuf_getmaxsize(c->bufhard) / 2; 1419 SLIST_FOREACH(pce, &c->children, link) { 1420 child = pce->channel; 1421 CHN_LOCK(child); 1422 if (sndbuf_getblksz(child->bufhard) < blksz) 1423 blksz = sndbuf_getblksz(child->bufhard); 1424 CHN_UNLOCK(child); 1425 } 1426 chn_setblocksize(c, 2, blksz); 1427 } 1428 if (flags & CHN_N_TRIGGER) { 1429 int nrun; 1430 /* 1431 * scan the children, and figure out if any are running 1432 * if so, we need to be running, otherwise we need to be stopped 1433 * if we aren't in our target sstate, move to it 1434 */ 1435 nrun = 0; 1436 SLIST_FOREACH(pce, &c->children, link) { 1437 child = pce->channel; 1438 CHN_LOCK(child); 1439 if (child->flags & CHN_F_TRIGGERED) 1440 nrun = 1; 1441 CHN_UNLOCK(child); 1442 } 1443 if (nrun && !run) 1444 chn_start(c, 1); 1445 if (!nrun && run) 1446 chn_abort(c); 1447 } 1448 CHN_UNLOCK(c); 1449 return 0; 1450} 1451 1452void 1453chn_lock(struct pcm_channel *c) 1454{ 1455 CHN_LOCK(c); 1456} 1457 1458void 1459chn_unlock(struct pcm_channel *c) 1460{ 1461 CHN_UNLOCK(c); 1462} 1463