The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/sound/pcm/channel.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

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

Cache object: 2f2ea7cd8748bf1edc37718cfb396837


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.