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


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

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

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

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

Cache object: 8abbe7988b436285e271f996fc107cdc


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