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

Cache object: b35ce141cbc92afff2daf23884439630


[ 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.