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

Cache object: 36cc2f13726537fcbb742e6a0b105a4f


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