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.0/sys/dev/sound/pcm/channel.c 107237 2002-11-25 17:17:43Z cg $");
   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 (SEL_WAITING(sndbuf_getsel(bs)) && 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 thread *td)
  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(td, 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 
  678         b = NULL;
  679         bs = NULL;
  680         c->devinfo = NULL;
  681         c->feeder = NULL;
  682 
  683         ret = EINVAL;
  684         fc = feeder_getclass(NULL);
  685         if (fc == NULL)
  686                 goto out;
  687         if (chn_addfeeder(c, fc, NULL))
  688                 goto out;
  689 
  690         ret = ENOMEM;
  691         b = sndbuf_create(c->dev, c->name, "primary");
  692         if (b == NULL)
  693                 goto out;
  694         bs = sndbuf_create(c->dev, c->name, "secondary");
  695         if (bs == NULL)
  696                 goto out;
  697         sndbuf_setup(bs, NULL, 0);
  698         c->bufhard = b;
  699         c->bufsoft = bs;
  700         c->flags = 0;
  701         c->feederflags = 0;
  702 
  703         ret = ENODEV;
  704         c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir);
  705         if (c->devinfo == NULL)
  706                 goto out;
  707 
  708         ret = ENOMEM;
  709         if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
  710                 goto out;
  711 
  712         ret = chn_setdir(c, dir);
  713         if (ret)
  714                 goto out;
  715 
  716         ret = sndbuf_setfmt(b, AFMT_U8);
  717         if (ret)
  718                 goto out;
  719 
  720         ret = sndbuf_setfmt(bs, AFMT_U8);
  721         if (ret)
  722                 goto out;
  723 
  724 
  725 out:
  726         if (ret) {
  727                 if (c->devinfo) {
  728                         if (CHANNEL_FREE(c->methods, c->devinfo))
  729                                 sndbuf_free(b);
  730                 }
  731                 if (bs)
  732                         sndbuf_destroy(bs);
  733                 if (b)
  734                         sndbuf_destroy(b);
  735                 c->flags |= CHN_F_DEAD;
  736                 chn_lockdestroy(c);
  737 
  738                 return ret;
  739         }
  740 
  741         return 0;
  742 }
  743 
  744 int
  745 chn_kill(struct pcm_channel *c)
  746 {
  747         struct snd_dbuf *b = c->bufhard;
  748         struct snd_dbuf *bs = c->bufsoft;
  749 
  750         if (c->flags & CHN_F_TRIGGERED)
  751                 chn_trigger(c, PCMTRIG_ABORT);
  752         while (chn_removefeeder(c) == 0);
  753         if (CHANNEL_FREE(c->methods, c->devinfo))
  754                 sndbuf_free(b);
  755         c->flags |= CHN_F_DEAD;
  756         sndbuf_destroy(bs);
  757         sndbuf_destroy(b);
  758         chn_lockdestroy(c);
  759         return 0;
  760 }
  761 
  762 int
  763 chn_setdir(struct pcm_channel *c, int dir)
  764 {
  765         struct snd_dbuf *b = c->bufhard;
  766         int r;
  767 
  768         CHN_LOCKASSERT(c);
  769         c->direction = dir;
  770         r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
  771         if (!r && ISA_DMA(b))
  772                 sndbuf_isadmasetdir(b, c->direction);
  773         return r;
  774 }
  775 
  776 int
  777 chn_setvolume(struct pcm_channel *c, int left, int right)
  778 {
  779         CHN_LOCKASSERT(c);
  780         /* could add a feeder for volume changing if channel returns -1 */
  781         c->volume = (left << 8) | right;
  782         return 0;
  783 }
  784 
  785 static int
  786 chn_tryspeed(struct pcm_channel *c, int speed)
  787 {
  788         struct pcm_feeder *f;
  789         struct snd_dbuf *b = c->bufhard;
  790         struct snd_dbuf *bs = c->bufsoft;
  791         struct snd_dbuf *x;
  792         int r, delta;
  793 
  794         CHN_LOCKASSERT(c);
  795         DEB(printf("setspeed, channel %s\n", c->name));
  796         DEB(printf("want speed %d, ", speed));
  797         if (speed <= 0)
  798                 return EINVAL;
  799         if (CANCHANGE(c)) {
  800                 r = 0;
  801                 c->speed = speed;
  802                 sndbuf_setspd(bs, speed);
  803                 RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
  804                 DEB(printf("try speed %d, ", speed));
  805                 sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed));
  806                 DEB(printf("got speed %d\n", sndbuf_getspd(b)));
  807 
  808                 delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
  809                 if (delta < 0)
  810                         delta = -delta;
  811 
  812                 c->feederflags &= ~(1 << FEEDER_RATE);
  813                 if (delta > 500)
  814                         c->feederflags |= 1 << FEEDER_RATE;
  815                 else
  816                         sndbuf_setspd(bs, sndbuf_getspd(b));
  817 
  818                 r = chn_buildfeeder(c);
  819                 DEB(printf("r = %d\n", r));
  820                 if (r)
  821                         goto out;
  822 
  823                 r = chn_setblocksize(c, 0, 0);
  824                 if (r)
  825                         goto out;
  826 
  827                 if (!(c->feederflags & (1 << FEEDER_RATE)))
  828                         goto out;
  829 
  830                 r = EINVAL;
  831                 f = chn_findfeeder(c, FEEDER_RATE);
  832                 DEB(printf("feedrate = %p\n", f));
  833                 if (f == NULL)
  834                         goto out;
  835 
  836                 x = (c->direction == PCMDIR_REC)? b : bs;
  837                 r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x));
  838                 DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r));
  839                 if (r)
  840                         goto out;
  841 
  842                 x = (c->direction == PCMDIR_REC)? bs : b;
  843                 r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
  844                 DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
  845 out:
  846                 DEB(printf("setspeed done, r = %d\n", r));
  847                 return r;
  848         } else
  849                 return EINVAL;
  850 }
  851 
  852 int
  853 chn_setspeed(struct pcm_channel *c, int speed)
  854 {
  855         int r, oldspeed = c->speed;
  856 
  857         r = chn_tryspeed(c, speed);
  858         if (r) {
  859                 DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
  860                 chn_tryspeed(c, oldspeed);
  861         }
  862         return r;
  863 }
  864 
  865 static int
  866 chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
  867 {
  868         struct snd_dbuf *b = c->bufhard;
  869         struct snd_dbuf *bs = c->bufsoft;
  870         int r;
  871 
  872         CHN_LOCKASSERT(c);
  873         if (CANCHANGE(c)) {
  874                 DEB(printf("want format %d\n", fmt));
  875                 c->format = fmt;
  876                 r = chn_buildfeeder(c);
  877                 if (r == 0) {
  878                         sndbuf_setfmt(bs, c->format);
  879                         chn_resetbuf(c);
  880                         r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b));
  881                         if (r == 0)
  882                                 r = chn_tryspeed(c, c->speed);
  883                 }
  884                 return r;
  885         } else
  886                 return EINVAL;
  887 }
  888 
  889 int
  890 chn_setformat(struct pcm_channel *c, u_int32_t fmt)
  891 {
  892         u_int32_t oldfmt = c->format;
  893         int r;
  894 
  895         r = chn_tryformat(c, fmt);
  896         if (r) {
  897                 DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
  898                 chn_tryformat(c, oldfmt);
  899         }
  900         return r;
  901 }
  902 
  903 int
  904 chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
  905 {
  906         struct snd_dbuf *b = c->bufhard;
  907         struct snd_dbuf *bs = c->bufsoft;
  908         int bufsz, irqhz, tmp, ret;
  909 
  910         CHN_LOCKASSERT(c);
  911         if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED))
  912                 return EINVAL;
  913 
  914         ret = 0;
  915         DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
  916         if (blksz == 0 || blksz == -1) {
  917                 if (blksz == -1)
  918                         c->flags &= ~CHN_F_HAS_SIZE;
  919                 if (!(c->flags & CHN_F_HAS_SIZE)) {
  920                         blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate;
  921                         tmp = 32;
  922                         while (tmp <= blksz)
  923                                 tmp <<= 1;
  924                         tmp >>= 1;
  925                         blksz = tmp;
  926                         blkcnt = CHN_2NDBUFMAXSIZE / blksz;
  927 
  928                         RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
  929                         RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
  930                         DEB(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz));
  931                 } else {
  932                         blkcnt = sndbuf_getblkcnt(bs);
  933                         blksz = sndbuf_getblksz(bs);
  934                         DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz));
  935                 }
  936         } else {
  937                 ret = EINVAL;
  938                 if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
  939                         goto out;
  940                 ret = 0;
  941                 c->flags |= CHN_F_HAS_SIZE;
  942         }
  943 
  944         bufsz = blkcnt * blksz;
  945 
  946         ret = sndbuf_remalloc(bs, blkcnt, blksz);
  947         if (ret)
  948                 goto out;
  949 
  950         /* adjust for different hw format/speed */
  951         irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs);
  952         DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
  953         RANGE(irqhz, 16, 512);
  954 
  955         sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz);
  956 
  957         /* round down to 2^x */
  958         blksz = 32;
  959         while (blksz <= sndbuf_getblksz(b))
  960                 blksz <<= 1;
  961         blksz >>= 1;
  962 
  963         /* round down to fit hw buffer size */
  964         RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
  965         DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b)));
  966 
  967         sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
  968 
  969         irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
  970         DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
  971 
  972         chn_resetbuf(c);
  973 out:
  974         return ret;
  975 }
  976 
  977 int
  978 chn_trigger(struct pcm_channel *c, int go)
  979 {
  980         struct snd_dbuf *b = c->bufhard;
  981         int ret;
  982 
  983         CHN_LOCKASSERT(c);
  984         if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
  985                 sndbuf_isadmabounce(b);
  986         ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
  987 
  988         return ret;
  989 }
  990 
  991 int
  992 chn_getptr(struct pcm_channel *c)
  993 {
  994         int hwptr;
  995         int a = (1 << c->align) - 1;
  996 
  997         CHN_LOCKASSERT(c);
  998         hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
  999         /* don't allow unaligned values in the hwa ptr */
 1000 #if 1
 1001         hwptr &= ~a ; /* Apply channel align mask */
 1002 #endif
 1003         hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
 1004         return hwptr;
 1005 }
 1006 
 1007 struct pcmchan_caps *
 1008 chn_getcaps(struct pcm_channel *c)
 1009 {
 1010         CHN_LOCKASSERT(c);
 1011         return CHANNEL_GETCAPS(c->methods, c->devinfo);
 1012 }
 1013 
 1014 u_int32_t
 1015 chn_getformats(struct pcm_channel *c)
 1016 {
 1017         u_int32_t *fmtlist, fmts;
 1018         int i;
 1019 
 1020         fmtlist = chn_getcaps(c)->fmtlist;
 1021         fmts = 0;
 1022         for (i = 0; fmtlist[i]; i++)
 1023                 fmts |= fmtlist[i];
 1024 
 1025         /* report software-supported formats */
 1026         if (report_soft_formats)
 1027                 fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U16_LE|AFMT_U16_BE|
 1028                     AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
 1029 
 1030         return fmts;
 1031 }
 1032 
 1033 static int
 1034 chn_buildfeeder(struct pcm_channel *c)
 1035 {
 1036         struct feeder_class *fc;
 1037         struct pcm_feederdesc desc;
 1038         u_int32_t tmp[2], type, flags, hwfmt;
 1039         int err;
 1040 
 1041         CHN_LOCKASSERT(c);
 1042         while (chn_removefeeder(c) == 0);
 1043         KASSERT((c->feeder == NULL), ("feeder chain not empty"));
 1044 
 1045         c->align = sndbuf_getalign(c->bufsoft);
 1046 
 1047         if (SLIST_EMPTY(&c->children)) {
 1048                 fc = feeder_getclass(NULL);
 1049                 KASSERT(fc != NULL, ("can't find root feeder"));
 1050 
 1051                 err = chn_addfeeder(c, fc, NULL);
 1052                 if (err) {
 1053                         DEB(printf("can't add root feeder, err %d\n", err));
 1054 
 1055                         return err;
 1056                 }
 1057                 c->feeder->desc->out = c->format;
 1058         } else {
 1059                 desc.type = FEEDER_MIXER;
 1060                 desc.in = 0;
 1061                 desc.out = c->format;
 1062                 desc.flags = 0;
 1063                 fc = feeder_getclass(&desc);
 1064                 if (fc == NULL) {
 1065                         DEB(printf("can't find vchan feeder\n"));
 1066 
 1067                         return EOPNOTSUPP;
 1068                 }
 1069 
 1070                 err = chn_addfeeder(c, fc, &desc);
 1071                 if (err) {
 1072                         DEB(printf("can't add vchan feeder, err %d\n", err));
 1073 
 1074                         return err;
 1075                 }
 1076         }
 1077         flags = c->feederflags;
 1078 
 1079         DEB(printf("not mapped, feederflags %x\n", flags));
 1080 
 1081         for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
 1082                 if (flags & (1 << type)) {
 1083                         desc.type = type;
 1084                         desc.in = 0;
 1085                         desc.out = 0;
 1086                         desc.flags = 0;
 1087                         DEB(printf("find feeder type %d, ", type));
 1088                         fc = feeder_getclass(&desc);
 1089                         DEB(printf("got %p\n", fc));
 1090                         if (fc == NULL) {
 1091                                 DEB(printf("can't find required feeder type %d\n", type));
 1092 
 1093                                 return EOPNOTSUPP;
 1094                         }
 1095 
 1096                         if (c->feeder->desc->out != fc->desc->in) {
 1097                                 DEB(printf("build fmtchain from %x to %x: ", c->feeder->desc->out, fc->desc->in));
 1098                                 tmp[0] = fc->desc->in;
 1099                                 tmp[1] = 0;
 1100                                 if (chn_fmtchain(c, tmp) == 0) {
 1101                                         DEB(printf("failed\n"));
 1102 
 1103                                         return ENODEV;
 1104                                 }
 1105                                 DEB(printf("ok\n"));
 1106                         }
 1107 
 1108                         err = chn_addfeeder(c, fc, fc->desc);
 1109                         if (err) {
 1110                                 DEB(printf("can't add feeder %p, output %x, err %d\n", fc, fc->desc->out, err));
 1111 
 1112                                 return err;
 1113                         }
 1114                         DEB(printf("added feeder %p, output %x\n", fc, c->feeder->desc->out));
 1115                 }
 1116         }
 1117 
 1118         if (fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) {
 1119                 hwfmt = c->feeder->desc->out;
 1120         } else {
 1121                 if (c->direction == PCMDIR_REC) {
 1122                         tmp[0] = c->format;
 1123                         tmp[1] = 0;
 1124                         hwfmt = chn_fmtchain(c, tmp);
 1125                 } else {
 1126 #if 0
 1127                         u_int32_t *x = chn_getcaps(c)->fmtlist;
 1128                         printf("acceptable formats for %s:\n", c->name);
 1129                         while (*x) {
 1130                                 printf("[%8x] ", *x);
 1131                                 x++;
 1132                         }
 1133 #endif
 1134                         hwfmt = chn_fmtchain(c, chn_getcaps(c)->fmtlist);
 1135                 }
 1136         }
 1137 
 1138         if (hwfmt == 0)
 1139                 return ENODEV;
 1140 
 1141         sndbuf_setfmt(c->bufhard, hwfmt);
 1142 
 1143         return 0;
 1144 }
 1145 
 1146 int
 1147 chn_notify(struct pcm_channel *c, u_int32_t flags)
 1148 {
 1149         struct pcmchan_children *pce;
 1150         struct pcm_channel *child;
 1151         int run;
 1152 
 1153         if (SLIST_EMPTY(&c->children))
 1154                 return ENODEV;
 1155 
 1156         run = (c->flags & CHN_F_TRIGGERED)? 1 : 0;
 1157         /*
 1158          * if the hwchan is running, we can't change its rate, format or
 1159          * blocksize
 1160          */
 1161         if (run)
 1162                 flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
 1163 
 1164         if (flags & CHN_N_RATE) {
 1165                 /*
 1166                  * we could do something here, like scan children and decide on
 1167                  * the most appropriate rate to mix at, but we don't for now
 1168                  */
 1169         }
 1170         if (flags & CHN_N_FORMAT) {
 1171                 /*
 1172                  * we could do something here, like scan children and decide on
 1173                  * the most appropriate mixer feeder to use, but we don't for now
 1174                  */
 1175         }
 1176         if (flags & CHN_N_VOLUME) {
 1177                 /*
 1178                  * we could do something here but we don't for now
 1179                  */
 1180         }
 1181         if (flags & CHN_N_BLOCKSIZE) {
 1182                 int blksz;
 1183                 /*
 1184                  * scan the children, find the lowest blocksize and use that
 1185                  * for the hard blocksize
 1186                  */
 1187                 blksz = sndbuf_getmaxsize(c->bufhard) / 2;
 1188                 SLIST_FOREACH(pce, &c->children, link) {
 1189                         child = pce->channel;
 1190                         if (sndbuf_getblksz(child->bufhard) < blksz)
 1191                                 blksz = sndbuf_getblksz(child->bufhard);
 1192                 }
 1193                 chn_setblocksize(c, 2, blksz);
 1194         }
 1195         if (flags & CHN_N_TRIGGER) {
 1196                 int nrun;
 1197                 /*
 1198                  * scan the children, and figure out if any are running
 1199                  * if so, we need to be running, otherwise we need to be stopped
 1200                  * if we aren't in our target sstate, move to it
 1201                  */
 1202                 nrun = 0;
 1203                 SLIST_FOREACH(pce, &c->children, link) {
 1204                         child = pce->channel;
 1205                         if (child->flags & CHN_F_TRIGGERED)
 1206                                 nrun = 1;
 1207                 }
 1208                 if (nrun && !run)
 1209                         chn_start(c, 1);
 1210                 if (!nrun && run)
 1211                         chn_abort(c);
 1212         }
 1213         return 0;
 1214 }

Cache object: 2bb6e4c97561272df433d7b0ae8e7f76


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