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

Cache object: 41e6182e6357b4aba2e7a8b5b64facad


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