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/vchan.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) 2001 Cameron Grant <cg@FreeBSD.org>
    3  * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org>
    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 /* Almost entirely rewritten to add multi-format/channels mixing support. */
   29 
   30 #include <dev/sound/pcm/sound.h>
   31 #include <dev/sound/pcm/vchan.h>
   32 #include "feeder_if.h"
   33 
   34 SND_DECLARE_FILE("$FreeBSD$");
   35 
   36 MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder");
   37 
   38 typedef uint32_t (*feed_vchan_mixer)(uint8_t *, uint8_t *, uint32_t);
   39 
   40 struct vchinfo {
   41         struct pcm_channel *channel;
   42         struct pcmchan_caps caps;
   43         uint32_t fmtlist[2];
   44         int trigger;
   45 };
   46 
   47 /* support everything (mono / stereo), except a-law / mu-law */
   48 static struct afmtstr_table vchan_supported_fmts[] = {
   49         {    "u8", AFMT_U8     }, {    "s8", AFMT_S8     },
   50         { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE },
   51         { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE },
   52         { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE },
   53         { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE },
   54         { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE },
   55         { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE },
   56         {    NULL, 0           },
   57 };
   58 
   59 /* alias table, shorter. */
   60 static const struct {
   61         char *alias, *fmtstr;
   62 } vchan_fmtstralias[] = {
   63         {  "8", "u8"    }, { "16", "s16le" },
   64         { "24", "s24le" }, { "32", "s32le" },
   65         { NULL, NULL    },
   66 };
   67 
   68 #define vchan_valid_format(fmt) \
   69         afmt2afmtstr(vchan_supported_fmts, fmt, NULL, 0, 0, \
   70         AFMTSTR_STEREO_RETURN)
   71 #define vchan_valid_strformat(strfmt) \
   72         afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN);
   73 
   74 /*
   75  * Need specialized WRITE macros since 32bit might involved saturation
   76  * if calculation is done within 32bit arithmetic.
   77  */
   78 #define VCHAN_PCM_WRITE_S8_NE(b8, val)          PCM_WRITE_S8(b8, val)
   79 #define VCHAN_PCM_WRITE_S16_LE(b8, val)         PCM_WRITE_S16_LE(b8, val)
   80 #define VCHAN_PCM_WRITE_S24_LE(b8, val)         PCM_WRITE_S24_LE(b8, val)
   81 #define VCHAN_PCM_WRITE_S32_LE(b8, val)         _PCM_WRITE_S32_LE(b8, val)
   82 #define VCHAN_PCM_WRITE_S16_BE(b8, val)         PCM_WRITE_S16_BE(b8, val)
   83 #define VCHAN_PCM_WRITE_S24_BE(b8, val)         PCM_WRITE_S24_BE(b8, val)
   84 #define VCHAN_PCM_WRITE_S32_BE(b8, val)         _PCM_WRITE_S32_BE(b8, val)
   85 #define VCHAN_PCM_WRITE_U8_NE(b8, val)          PCM_WRITE_U8(b8, val)
   86 #define VCHAN_PCM_WRITE_U16_LE(b8, val)         PCM_WRITE_U16_LE(b8, val)
   87 #define VCHAN_PCM_WRITE_U24_LE(b8, val)         PCM_WRITE_U24_LE(b8, val)
   88 #define VCHAN_PCM_WRITE_U32_LE(b8, val)         _PCM_WRITE_U32_LE(b8, val)
   89 #define VCHAN_PCM_WRITE_U16_BE(b8, val)         PCM_WRITE_U16_BE(b8, val)
   90 #define VCHAN_PCM_WRITE_U24_BE(b8, val)         PCM_WRITE_U24_BE(b8, val)
   91 #define VCHAN_PCM_WRITE_U32_BE(b8, val)         _PCM_WRITE_U32_BE(b8, val)
   92 
   93 #define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS)   \
   94 static uint32_t                                                                 \
   95 feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(uint8_t *to, uint8_t *tmp,              \
   96                                                         uint32_t count)         \
   97 {                                                                               \
   98         int32_t x, y;                                                           \
   99         VCHAN_INTCAST z;                                                        \
  100         int i;                                                                  \
  101                                                                                 \
  102         i = count;                                                              \
  103         tmp += i;                                                               \
  104         to += i;                                                                \
  105                                                                                 \
  106         do {                                                                    \
  107                 tmp -= PCM_##FMTBIT##_BPS;                                      \
  108                 to -= PCM_##FMTBIT##_BPS;                                       \
  109                 i -= PCM_##FMTBIT##_BPS;                                        \
  110                 x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp);                    \
  111                 y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to);                     \
  112                 z = (VCHAN_INTCAST)x + y;                                       \
  113                 x = PCM_CLAMP_##SIGN##FMTBIT(z);                                \
  114                 VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x);               \
  115         } while (i != 0);                                                       \
  116                                                                                 \
  117         return (count);                                                         \
  118 }
  119 
  120 FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne)
  121 FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le)
  122 FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le)
  123 FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le)
  124 FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be)
  125 FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be)
  126 FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be)
  127 FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne)
  128 FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le)
  129 FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le)
  130 FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le)
  131 FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be)
  132 FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be)
  133 FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be)
  134 
  135 struct feed_vchan_info {
  136         uint32_t format;
  137         int bps;
  138         feed_vchan_mixer mix;
  139 };
  140 
  141 static struct feed_vchan_info feed_vchan_info_tbl[] = {
  142         { AFMT_S8,     PCM_8_BPS,  feed_vchan_mix_s8ne },
  143         { AFMT_S16_LE, PCM_16_BPS, feed_vchan_mix_s16le },
  144         { AFMT_S24_LE, PCM_24_BPS, feed_vchan_mix_s24le },
  145         { AFMT_S32_LE, PCM_32_BPS, feed_vchan_mix_s32le },
  146         { AFMT_S16_BE, PCM_16_BPS, feed_vchan_mix_s16be },
  147         { AFMT_S24_BE, PCM_24_BPS, feed_vchan_mix_s24be },
  148         { AFMT_S32_BE, PCM_32_BPS, feed_vchan_mix_s32be },
  149         { AFMT_U8,     PCM_8_BPS,  feed_vchan_mix_u8ne  },
  150         { AFMT_U16_LE, PCM_16_BPS, feed_vchan_mix_u16le },
  151         { AFMT_U24_LE, PCM_24_BPS, feed_vchan_mix_u24le },
  152         { AFMT_U32_LE, PCM_32_BPS, feed_vchan_mix_u32le },
  153         { AFMT_U16_BE, PCM_16_BPS, feed_vchan_mix_u16be },
  154         { AFMT_U24_BE, PCM_24_BPS, feed_vchan_mix_u24be },
  155         { AFMT_U32_BE, PCM_32_BPS, feed_vchan_mix_u32be },
  156 };
  157 
  158 #define FVCHAN_DATA(i, c)       ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf)))
  159 #define FVCHAN_INFOIDX(m)       (((m) >> 4) & 0x1f)
  160 #define FVCHAN_CHANNELS(m)      ((m) & 0xf)
  161 
  162 static int
  163 feed_vchan_init(struct pcm_feeder *f)
  164 {
  165         int i, channels;
  166 
  167         if (f->desc->out != f->desc->in)
  168                 return (EINVAL);
  169 
  170         channels = (f->desc->out & AFMT_STEREO) ? 2 : 1;
  171 
  172         for (i = 0; i < sizeof(feed_vchan_info_tbl) /
  173             sizeof(feed_vchan_info_tbl[0]); i++) {
  174                 if ((f->desc->out & ~AFMT_STEREO) ==
  175                     feed_vchan_info_tbl[i].format) {
  176                         f->data = (void *)FVCHAN_DATA(i, channels);
  177                         return (0);
  178                 }
  179         }
  180 
  181         return (-1);
  182 }
  183 
  184 static __inline int
  185 feed_vchan_rec(struct pcm_channel *c)
  186 {
  187         struct pcm_channel *ch;
  188         struct snd_dbuf *b, *bs;
  189         int cnt, rdy;
  190 
  191         /*
  192          * Reset ready and moving pointer. We're not using bufsoft
  193          * anywhere since its sole purpose is to become the primary
  194          * distributor for the recorded buffer and also as an interrupt
  195          * threshold progress indicator.
  196          */
  197         b = c->bufsoft;
  198         b->rp = 0;
  199         b->rl = 0;
  200         cnt = sndbuf_getsize(b);
  201 
  202         do {
  203                 cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf, cnt,
  204                     c->bufhard);
  205                 if (cnt != 0) {
  206                         sndbuf_acquire(b, b->tmpbuf, cnt);
  207                         cnt = sndbuf_getfree(b);
  208                 }
  209         } while (cnt != 0);
  210 
  211         /* Not enough data */
  212         if (b->rl < sndbuf_getbps(b)) {
  213                 b->rl = 0;
  214                 return (0);
  215         }
  216 
  217         /*
  218          * Keep track of ready and moving pointer since we will use
  219          * bufsoft over and over again, pretending nothing has happened.
  220          */
  221         rdy = b->rl;
  222 
  223         CHN_FOREACH(ch, c, children.busy) {
  224                 CHN_LOCK(ch);
  225                 if (!(ch->flags & CHN_F_TRIGGERED)) {
  226                         CHN_UNLOCK(ch);
  227                         continue;
  228                 }
  229                 bs = ch->bufsoft;
  230                 if (ch->flags & CHN_F_MAPPED)
  231                         sndbuf_dispose(bs, NULL, sndbuf_getready(bs));
  232                 cnt = sndbuf_getfree(bs);
  233                 if (cnt < sndbuf_getbps(bs)) {
  234                         CHN_UNLOCK(ch);
  235                         continue;
  236                 }
  237                 do {
  238                         cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, cnt, b);
  239                         if (cnt != 0) {
  240                                 sndbuf_acquire(bs, bs->tmpbuf, cnt);
  241                                 cnt = sndbuf_getfree(bs);
  242                         }
  243                 } while (cnt != 0);
  244                 /*
  245                  * Not entirely flushed out...
  246                  */
  247                 if (b->rl != 0)
  248                         ch->xruns++;
  249                 CHN_UNLOCK(ch);
  250                 /*
  251                  * Rewind buffer position for next virtual channel.
  252                  */
  253                 b->rp = 0;
  254                 b->rl = rdy;
  255         }
  256 
  257         /*
  258          * Set ready pointer to indicate that our children are ready
  259          * to be woken up, also as an interrupt threshold progress
  260          * indicator.
  261          */
  262         b->rl = 1;
  263 
  264         /*
  265          * Return 0 to bail out early from sndbuf_feed() loop.
  266          * No need to increase feedcount counter since part of this
  267          * feeder chains already include feed_root().
  268          */
  269         return (0);
  270 }
  271 
  272 static int
  273 feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
  274     uint32_t count, void *source)
  275 {
  276         struct feed_vchan_info *info;
  277         struct snd_dbuf *src = source;
  278         struct pcm_channel *ch;
  279         uint32_t cnt, mcnt, rcnt, sz;
  280         uint8_t *tmp;
  281 
  282         if (c->direction == PCMDIR_REC)
  283                 return (feed_vchan_rec(c));
  284 
  285         sz = sndbuf_getsize(src);
  286         if (sz < count)
  287                 count = sz;
  288 
  289         info = &feed_vchan_info_tbl[FVCHAN_INFOIDX((intptr_t)f->data)];
  290         sz = info->bps * FVCHAN_CHANNELS((intptr_t)f->data);
  291         count -= count % sz;
  292         if (count < sz)
  293                 return (0);
  294 
  295         /*
  296          * we are going to use our source as a temporary buffer since it's
  297          * got no other purpose.  we obtain our data by traversing the channel
  298          * list of children and calling vchan_mix_* to mix count bytes from
  299          * each into our destination buffer, b
  300          */
  301         tmp = sndbuf_getbuf(src);
  302         rcnt = 0;
  303         mcnt = 0;
  304 
  305         CHN_FOREACH(ch, c, children.busy) {
  306                 CHN_LOCK(ch);
  307                 if (!(ch->flags & CHN_F_TRIGGERED)) {
  308                         CHN_UNLOCK(ch);
  309                         continue;
  310                 }
  311                 if ((ch->flags & CHN_F_MAPPED) && !(ch->flags & CHN_F_CLOSING))
  312                         sndbuf_acquire(ch->bufsoft, NULL,
  313                             sndbuf_getfree(ch->bufsoft));
  314                 if (rcnt == 0) {
  315                         rcnt = FEEDER_FEED(ch->feeder, ch, b, count,
  316                             ch->bufsoft);
  317                         rcnt -= rcnt % sz;
  318                         mcnt = count - rcnt;
  319                 } else {
  320                         cnt = FEEDER_FEED(ch->feeder, ch, tmp, count,
  321                             ch->bufsoft);
  322                         cnt -= cnt % sz;
  323                         if (cnt != 0) {
  324                                 if (mcnt != 0) {
  325                                         memset(b + rcnt,
  326                                             sndbuf_zerodata(f->desc->out),
  327                                             mcnt);
  328                                         mcnt = 0;
  329                                 }
  330                                 cnt = info->mix(b, tmp, cnt);
  331                                 if (cnt > rcnt)
  332                                         rcnt = cnt;
  333                         }
  334                 }
  335                 CHN_UNLOCK(ch);
  336         }
  337 
  338         if (++c->feedcount == 0)
  339                 c->feedcount = 2;
  340 
  341         return (rcnt);
  342 }
  343 
  344 static struct pcm_feederdesc feeder_vchan_desc[] = {
  345         {FEEDER_MIXER, AFMT_S8, AFMT_S8, 0},
  346         {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0},
  347         {FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0},
  348         {FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0},
  349         {FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0},
  350         {FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0},
  351         {FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0},
  352         {FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
  353         {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
  354         {FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
  355         {FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
  356         {FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
  357         {FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
  358         {FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
  359         {FEEDER_MIXER, AFMT_U8, AFMT_U8, 0},
  360         {FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0},
  361         {FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0},
  362         {FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0},
  363         {FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0},
  364         {FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0},
  365         {FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0},
  366         {FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
  367         {FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
  368         {FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
  369         {FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
  370         {FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
  371         {FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
  372         {FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
  373         {0, 0, 0, 0},
  374 };
  375 static kobj_method_t feeder_vchan_methods[] = {
  376         KOBJMETHOD(feeder_init,         feed_vchan_init),
  377         KOBJMETHOD(feeder_feed,         feed_vchan),
  378         {0, 0}
  379 };
  380 FEEDER_DECLARE(feeder_vchan, 2, NULL);
  381 
  382 /************************************************************/
  383 
  384 static void *
  385 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
  386     struct pcm_channel *c, int dir)
  387 {
  388         struct vchinfo *ch;
  389 
  390         KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC,
  391             ("vchan_init: bad direction"));
  392         KASSERT(c != NULL && c->parentchannel != NULL,
  393             ("vchan_init: bad channels"));
  394 
  395         ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
  396         ch->channel = c;
  397         ch->trigger = PCMTRIG_STOP;
  398 
  399         c->flags |= CHN_F_VIRTUAL;
  400 
  401         return (ch);
  402 }
  403 
  404 static int
  405 vchan_free(kobj_t obj, void *data)
  406 {
  407         free(data, M_DEVBUF);
  408 
  409         return (0);
  410 }
  411 
  412 static int
  413 vchan_setformat(kobj_t obj, void *data, uint32_t format)
  414 {
  415         struct vchinfo *ch = data;
  416 
  417         if (fmtvalid(format, ch->fmtlist) == 0)
  418                 return (-1);
  419 
  420         return (0);
  421 }
  422 
  423 static int
  424 vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
  425 {
  426         struct vchinfo *ch = data;
  427         struct pcm_channel *p = ch->channel->parentchannel;
  428 
  429         return (sndbuf_getspd(p->bufsoft));
  430 }
  431 
  432 static int
  433 vchan_trigger(kobj_t obj, void *data, int go)
  434 {
  435         struct vchinfo *ch = data;
  436         struct pcm_channel *c, *p;
  437         int err, otrigger;
  438 
  439         if (!PCMTRIG_COMMON(go) || go == ch->trigger)
  440                 return (0);
  441 
  442         c = ch->channel;
  443         p = c->parentchannel;
  444         otrigger = ch->trigger;
  445         ch->trigger = go;
  446 
  447         CHN_UNLOCK(c);
  448         CHN_LOCK(p);
  449 
  450         switch (go) {
  451         case PCMTRIG_START:
  452                 if (otrigger != PCMTRIG_START) {
  453                         CHN_INSERT_HEAD(p, c, children.busy);
  454                 }
  455                 break;
  456         case PCMTRIG_STOP:
  457         case PCMTRIG_ABORT:
  458                 if (otrigger == PCMTRIG_START) {
  459                         CHN_REMOVE(p, c, children.busy);
  460                 }
  461                 break;
  462         default:
  463                 break;
  464         }
  465 
  466         err = chn_notify(p, CHN_N_TRIGGER);
  467         CHN_UNLOCK(p);
  468         CHN_LOCK(c);
  469 
  470         return (err);
  471 }
  472 
  473 static struct pcmchan_caps *
  474 vchan_getcaps(kobj_t obj, void *data)
  475 {
  476         struct vchinfo *ch = data;
  477         struct pcm_channel *c, *p;
  478         uint32_t fmt;
  479 
  480         c = ch->channel;
  481         p = c->parentchannel;
  482         ch->caps.minspeed = sndbuf_getspd(p->bufsoft);
  483         ch->caps.maxspeed = ch->caps.minspeed;
  484         ch->caps.caps = 0;
  485         ch->fmtlist[1] = 0;
  486         fmt = sndbuf_getfmt(p->bufsoft);
  487         if (fmt != vchan_valid_format(fmt)) {
  488                 device_printf(c->dev,
  489                             "%s: WARNING: invalid vchan format! (0x%08x)\n",
  490                             __func__, fmt);
  491                 fmt = VCHAN_DEFAULT_AFMT;
  492         }
  493         ch->fmtlist[0] = fmt;
  494         ch->caps.fmtlist = ch->fmtlist;
  495 
  496         return (&ch->caps);
  497 }
  498 
  499 static kobj_method_t vchan_methods[] = {
  500         KOBJMETHOD(channel_init,                vchan_init),
  501         KOBJMETHOD(channel_free,                vchan_free),
  502         KOBJMETHOD(channel_setformat,           vchan_setformat),
  503         KOBJMETHOD(channel_setspeed,            vchan_setspeed),
  504         KOBJMETHOD(channel_trigger,             vchan_trigger),
  505         KOBJMETHOD(channel_getcaps,             vchan_getcaps),
  506         {0, 0}
  507 };
  508 CHANNEL_DECLARE(vchan);
  509 
  510 /* 
  511  * On the fly vchan rate settings
  512  */
  513 #ifdef SND_DYNSYSCTL
  514 static int
  515 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
  516 {
  517         struct snddev_info *d;
  518         struct pcm_channel *c, *ch = NULL;
  519         struct pcmchan_caps *caps;
  520         int *vchanrate, vchancount, direction, err, newspd;
  521 
  522         d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
  523         if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
  524                 return (EINVAL);
  525 
  526         pcm_lock(d);
  527         PCM_WAIT(d);
  528 
  529         switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
  530         case VCHAN_PLAY:
  531                 direction = PCMDIR_PLAY;
  532                 vchancount = d->pvchancount;
  533                 vchanrate = &d->pvchanrate;
  534                 break;
  535         case VCHAN_REC:
  536                 direction = PCMDIR_REC;
  537                 vchancount = d->rvchancount;
  538                 vchanrate = &d->rvchanrate;
  539                 break;
  540         default:
  541                 pcm_unlock(d);
  542                 return (EINVAL);
  543                 break;
  544         }
  545 
  546         if (vchancount < 1) {
  547                 pcm_unlock(d);
  548                 return (EINVAL);
  549         }
  550 
  551         PCM_ACQUIRE(d);
  552         pcm_unlock(d);
  553 
  554         newspd = 0;
  555 
  556         CHN_FOREACH(c, d, channels.pcm) {
  557                 CHN_LOCK(c);
  558                 if (c->direction == direction) {
  559                         if (c->flags & CHN_F_VIRTUAL) {
  560                                 /* Sanity check */
  561                                 if (ch != NULL && ch != c->parentchannel) {
  562                                         CHN_UNLOCK(c);
  563                                         PCM_RELEASE_QUICK(d);
  564                                         return (EINVAL);
  565                                 }
  566                         } else if (c->flags & CHN_F_HAS_VCHAN) {
  567                                 /* No way!! */
  568                                 if (ch != NULL) {
  569                                         CHN_UNLOCK(c);
  570                                         PCM_RELEASE_QUICK(d);
  571                                         return (EINVAL);
  572                                 }
  573                                 ch = c;
  574                                 newspd = ch->speed;
  575                         }
  576                 }
  577                 CHN_UNLOCK(c);
  578         }
  579         if (ch == NULL) {
  580                 PCM_RELEASE_QUICK(d);
  581                 return (EINVAL);
  582         }
  583 
  584         err = sysctl_handle_int(oidp, &newspd, 0, req);
  585         if (err == 0 && req->newptr != NULL) {
  586                 if (newspd < 1 || newspd < feeder_rate_min ||
  587                     newspd > feeder_rate_max) {
  588                         PCM_RELEASE_QUICK(d);
  589                         return (EINVAL);
  590                 }
  591                 CHN_LOCK(ch);
  592                 if (feeder_rate_round) {
  593                         caps = chn_getcaps(ch);
  594                         if (caps == NULL || newspd < caps->minspeed ||
  595                             newspd > caps->maxspeed) {
  596                                 CHN_UNLOCK(ch);
  597                                 PCM_RELEASE_QUICK(d);
  598                                 return (EINVAL);
  599                         }
  600                 }
  601                 if (CHN_STOPPED(ch) && newspd != ch->speed) {
  602                         err = chn_setspeed(ch, newspd);
  603                         /*
  604                          * Try to avoid FEEDER_RATE on parent channel if the
  605                          * requested value is not supported by the hardware.
  606                          */
  607                         if (!err && feeder_rate_round &&
  608                             (ch->feederflags & (1 << FEEDER_RATE))) {
  609                                 newspd = sndbuf_getspd(ch->bufhard);
  610                                 err = chn_setspeed(ch, newspd);
  611                         }
  612                         if (err == 0)
  613                                 *vchanrate = newspd;
  614                 }
  615                 CHN_UNLOCK(ch);
  616         }
  617 
  618         PCM_RELEASE_QUICK(d);
  619 
  620         return (err);
  621 }
  622 
  623 static int
  624 sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
  625 {
  626         struct snddev_info *d;
  627         struct pcm_channel *c, *ch = NULL;
  628         uint32_t newfmt, spd;
  629         int *vchanformat, vchancount, direction, err, i;
  630         char fmtstr[AFMTSTR_MAXSZ];
  631 
  632         d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
  633         if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
  634                 return (EINVAL);
  635 
  636         pcm_lock(d);
  637         PCM_WAIT(d);
  638 
  639         switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
  640         case VCHAN_PLAY:
  641                 direction = PCMDIR_PLAY;
  642                 vchancount = d->pvchancount;
  643                 vchanformat = &d->pvchanformat;
  644                 break;
  645         case VCHAN_REC:
  646                 direction = PCMDIR_REC;
  647                 vchancount = d->rvchancount;
  648                 vchanformat = &d->rvchanformat;
  649                 break;
  650         default:
  651                 pcm_unlock(d);
  652                 return (EINVAL);
  653                 break;
  654         }
  655 
  656         if (vchancount < 1) {
  657                 pcm_unlock(d);
  658                 return (EINVAL);
  659         }
  660 
  661         PCM_ACQUIRE(d);
  662         pcm_unlock(d);
  663 
  664         CHN_FOREACH(c, d, channels.pcm) {
  665                 CHN_LOCK(c);
  666                 if (c->direction == direction) {
  667                         if (c->flags & CHN_F_VIRTUAL) {
  668                                 /* Sanity check */
  669                                 if (ch != NULL && ch != c->parentchannel) {
  670                                         CHN_UNLOCK(c);
  671                                         PCM_RELEASE_QUICK(d);
  672                                         return (EINVAL);
  673                                 }
  674                         } else if (c->flags & CHN_F_HAS_VCHAN) {
  675                                 /* No way!! */
  676                                 if (ch != NULL) {
  677                                         CHN_UNLOCK(c);
  678                                         PCM_RELEASE_QUICK(d);
  679                                         return (EINVAL);
  680                                 }
  681                                 ch = c;
  682                                 if (ch->format !=
  683                                     afmt2afmtstr(vchan_supported_fmts,
  684                                     ch->format, fmtstr, sizeof(fmtstr),
  685                                     AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) {
  686                                         strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT,
  687                                             sizeof(fmtstr));
  688                                 }
  689                         }
  690                 }
  691                 CHN_UNLOCK(c);
  692         }
  693         if (ch == NULL) {
  694                 PCM_RELEASE_QUICK(d);
  695                 return (EINVAL);
  696         }
  697 
  698         err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
  699         if (err == 0 && req->newptr != NULL) {
  700                 for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) {
  701                         if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) {
  702                                 strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr,
  703                                     sizeof(fmtstr));
  704                                 break;
  705                         }
  706                 }
  707                 newfmt = vchan_valid_strformat(fmtstr);
  708                 if (newfmt == 0) {
  709                         PCM_RELEASE_QUICK(d);
  710                         return (EINVAL);
  711                 }
  712                 CHN_LOCK(ch);
  713                 if (CHN_STOPPED(ch) && newfmt != ch->format) {
  714                         /* Get channel speed, before chn_reset() screw it. */
  715                         spd = ch->speed;
  716                         err = chn_reset(ch, newfmt);
  717                         if (err == 0)
  718                                 err = chn_setspeed(ch, spd);
  719                         if (err == 0)
  720                                 *vchanformat = newfmt;
  721                 }
  722                 CHN_UNLOCK(ch);
  723         }
  724 
  725         PCM_RELEASE_QUICK(d);
  726 
  727         return (err);
  728 }
  729 #endif
  730 
  731 /* virtual channel interface */
  732 
  733 #define VCHAN_FMT_HINT(x)       ((x) == PCMDIR_PLAY_VIRTUAL) ?          \
  734                                 "play.vchanformat" : "rec.vchanformat"
  735 #define VCHAN_SPD_HINT(x)       ((x) == PCMDIR_PLAY_VIRTUAL) ?          \
  736                                 "play.vchanrate" : "rec.vchanrate"
  737 
  738 int
  739 vchan_create(struct pcm_channel *parent, int num)
  740 {
  741         struct snddev_info *d = parent->parentsnddev;
  742         struct pcm_channel *ch, *tmp, *after;
  743         struct pcmchan_caps *parent_caps;
  744         uint32_t vchanfmt;
  745         int err, first, speed, r;
  746         int direction;
  747 
  748         PCM_BUSYASSERT(d);
  749 
  750         if (!(parent->flags & CHN_F_BUSY))
  751                 return (EBUSY);
  752 
  753         if (parent->direction == PCMDIR_PLAY) {
  754                 direction = PCMDIR_PLAY_VIRTUAL;
  755                 vchanfmt = d->pvchanformat;
  756                 speed = d->pvchanrate;
  757         } else if (parent->direction == PCMDIR_REC) {
  758                 direction = PCMDIR_REC_VIRTUAL;
  759                 vchanfmt = d->rvchanformat;
  760                 speed = d->rvchanrate;
  761         } else
  762                 return (EINVAL);
  763         CHN_UNLOCK(parent);
  764 
  765         /* create a new playback channel */
  766         pcm_lock(d);
  767         ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent);
  768         if (ch == NULL) {
  769                 pcm_unlock(d);
  770                 CHN_LOCK(parent);
  771                 return (ENODEV);
  772         }
  773 
  774         /* add us to our grandparent's channel list */
  775         err = pcm_chn_add(d, ch);
  776         pcm_unlock(d);
  777         if (err) {
  778                 pcm_chn_destroy(ch);
  779                 CHN_LOCK(parent);
  780                 return (err);
  781         }
  782 
  783         CHN_LOCK(parent);
  784         /* add us to our parent channel's children */
  785         first = CHN_EMPTY(parent, children);
  786         after = NULL;
  787         CHN_FOREACH(tmp, parent, children) {
  788                 if (CHN_CHAN(tmp) > CHN_CHAN(ch))
  789                         after = tmp;
  790                 else if (CHN_CHAN(tmp) < CHN_CHAN(ch))
  791                         break;
  792         }
  793         if (after != NULL) {
  794                 CHN_INSERT_AFTER(after, ch, children);
  795         } else {
  796                 CHN_INSERT_HEAD(parent, ch, children);
  797         }
  798         parent->flags |= CHN_F_HAS_VCHAN;
  799 
  800         if (first) {
  801                 parent_caps = chn_getcaps(parent);
  802                 if (parent_caps == NULL)
  803                         err = EINVAL;
  804 
  805                 if (!err) {
  806                         if (vchanfmt == 0) {
  807                                 const char *vfmt;
  808 
  809                                 CHN_UNLOCK(parent);
  810                                 r = resource_string_value(
  811                                     device_get_name(parent->dev),
  812                                     device_get_unit(parent->dev),
  813                                     VCHAN_FMT_HINT(direction),
  814                                     &vfmt);
  815                                 CHN_LOCK(parent);
  816                                 if (r != 0)
  817                                         vfmt = NULL;
  818                                 if (vfmt != NULL) {
  819                                         vchanfmt = vchan_valid_strformat(vfmt);
  820                                         for (r = 0; vchanfmt == 0 &&
  821                                             vchan_fmtstralias[r].alias != NULL;
  822                                             r++) {
  823                                                 if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) {
  824                                                         vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr);
  825                                                         break;
  826                                                 }
  827                                         }
  828                                 }
  829                                 if (vchanfmt == 0)
  830                                         vchanfmt = VCHAN_DEFAULT_AFMT;
  831                         }
  832                         err = chn_reset(parent, vchanfmt);
  833                 }
  834 
  835                 if (!err) {
  836                         /*
  837                          * This is very sad. Few soundcards advertised as being
  838                          * able to do (insanely) higher/lower speed, but in
  839                          * reality, they simply can't. At least, we give user chance
  840                          * to set sane value via kernel hints or sysctl.
  841                          */
  842                         if (speed < 1) {
  843                                 CHN_UNLOCK(parent);
  844                                 r = resource_int_value(
  845                                     device_get_name(parent->dev),
  846                                     device_get_unit(parent->dev),
  847                                     VCHAN_SPD_HINT(direction),
  848                                     &speed);
  849                                 CHN_LOCK(parent);
  850                                 if (r != 0) {
  851                                         /*
  852                                          * No saved value, no hint, NOTHING.
  853                                          *
  854                                          * Workaround for sb16 running
  855                                          * poorly at 45k / 49k.
  856                                          */
  857                                         switch (parent_caps->maxspeed) {
  858                                         case 45000:
  859                                         case 49000:
  860                                                 speed = 44100;
  861                                                 break;
  862                                         default:
  863                                                 speed = VCHAN_DEFAULT_SPEED;
  864                                                 if (speed > parent_caps->maxspeed)
  865                                                         speed = parent_caps->maxspeed;
  866                                                 break;
  867                                         }
  868                                         if (speed < parent_caps->minspeed)
  869                                                 speed = parent_caps->minspeed;
  870                                 }
  871                         }
  872 
  873                         if (feeder_rate_round) {
  874                                 /*
  875                                  * Limit speed based on driver caps.
  876                                  * This is supposed to help fixed rate, non-VRA
  877                                  * AC97 cards, but.. (see below)
  878                                  */
  879                                 if (speed < parent_caps->minspeed)
  880                                         speed = parent_caps->minspeed;
  881                                 if (speed > parent_caps->maxspeed)
  882                                         speed = parent_caps->maxspeed;
  883                         }
  884 
  885                         /*
  886                          * We still need to limit the speed between
  887                          * feeder_rate_min <-> feeder_rate_max. This is
  888                          * just an escape goat if all of the above failed
  889                          * miserably.
  890                          */
  891                         if (speed < feeder_rate_min)
  892                                 speed = feeder_rate_min;
  893                         if (speed > feeder_rate_max)
  894                                 speed = feeder_rate_max;
  895 
  896                         err = chn_setspeed(parent, speed);
  897                         /*
  898                          * Try to avoid FEEDER_RATE on parent channel if the
  899                          * requested value is not supported by the hardware.
  900                          */
  901                         if (!err && feeder_rate_round &&
  902                             (parent->feederflags & (1 << FEEDER_RATE))) {
  903                                 speed = sndbuf_getspd(parent->bufhard);
  904                                 err = chn_setspeed(parent, speed);
  905                         }
  906 
  907                         if (!err) {
  908                                 /*
  909                                  * Save new value.
  910                                  */
  911                                 CHN_UNLOCK(parent);
  912                                 if (direction == PCMDIR_PLAY_VIRTUAL) {
  913                                         d->pvchanformat = vchanfmt;
  914                                         d->pvchanrate = speed;
  915                                 } else {
  916                                         d->rvchanformat = vchanfmt;
  917                                         d->rvchanrate = speed;
  918                                 }
  919                                 CHN_LOCK(parent);
  920                         }
  921                 }
  922                 
  923                 if (err) {
  924                         CHN_REMOVE(parent, ch, children);
  925                         parent->flags &= ~CHN_F_HAS_VCHAN;
  926                         CHN_UNLOCK(parent);
  927                         pcm_lock(d);
  928                         if (pcm_chn_remove(d, ch) == 0) {
  929                                 pcm_unlock(d);
  930                                 pcm_chn_destroy(ch);
  931                         } else
  932                                 pcm_unlock(d);
  933                         CHN_LOCK(parent);
  934                         return (err);
  935                 }
  936         }
  937 
  938         return (0);
  939 }
  940 
  941 int
  942 vchan_destroy(struct pcm_channel *c)
  943 {
  944         struct pcm_channel *parent = c->parentchannel;
  945         struct snddev_info *d = parent->parentsnddev;
  946         uint32_t spd;
  947         int err;
  948 
  949         PCM_BUSYASSERT(d);
  950 
  951         CHN_LOCK(parent);
  952         if (!(parent->flags & CHN_F_BUSY)) {
  953                 CHN_UNLOCK(parent);
  954                 return (EBUSY);
  955         }
  956         if (CHN_EMPTY(parent, children)) {
  957                 CHN_UNLOCK(parent);
  958                 return (EINVAL);
  959         }
  960 
  961         /* remove us from our parent's children list */
  962         CHN_REMOVE(parent, c, children);
  963 
  964         if (CHN_EMPTY(parent, children)) {
  965                 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
  966                 spd = parent->speed;
  967                 if (chn_reset(parent, parent->format) == 0)
  968                         chn_setspeed(parent, spd);
  969         }
  970 
  971         CHN_UNLOCK(parent);
  972 
  973         /* remove us from our grandparent's channel list */
  974         pcm_lock(d);
  975         err = pcm_chn_remove(d, c);
  976         pcm_unlock(d);
  977 
  978         /* destroy ourselves */
  979         if (!err)
  980                 err = pcm_chn_destroy(c);
  981 
  982         return (err);
  983 }
  984 
  985 int
  986 vchan_initsys(device_t dev)
  987 {
  988 #ifdef SND_DYNSYSCTL
  989         struct snddev_info *d;
  990         int unit;
  991 
  992         unit = device_get_unit(dev);
  993         d = device_get_softc(dev);
  994 
  995         /* Play */
  996         SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
  997             SYSCTL_CHILDREN(d->play_sysctl_tree),
  998             OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW,
  999             VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
 1000             sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
 1001         SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
 1002             SYSCTL_CHILDREN(d->play_sysctl_tree),
 1003             OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW,
 1004             VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
 1005             sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate");
 1006         SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
 1007             SYSCTL_CHILDREN(d->play_sysctl_tree),
 1008             OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW,
 1009             VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
 1010             sysctl_hw_snd_vchanformat, "A", "virtual channel format");
 1011         /* Rec */
 1012         SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
 1013             SYSCTL_CHILDREN(d->rec_sysctl_tree),
 1014             OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW,
 1015             VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
 1016             sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
 1017         SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
 1018             SYSCTL_CHILDREN(d->rec_sysctl_tree),
 1019             OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW,
 1020             VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
 1021             sysctl_hw_snd_vchanrate, "I", "virtual channel base speed/rate");
 1022         SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
 1023             SYSCTL_CHILDREN(d->rec_sysctl_tree),
 1024             OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW,
 1025             VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
 1026             sysctl_hw_snd_vchanformat, "A", "virtual channel format");
 1027 #endif
 1028 
 1029         return (0);
 1030 }

Cache object: 3c62cd3e97e6c167ef7c994e1d7bc141


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