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

Cache object: 0cc0360fb617788ecb80eadeaf498913


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