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

Cache object: 42c2ec0631341cba8906589a25c8708e


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