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-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2001 Cameron Grant <cg@freebsd.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  *
   26  * $FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.17.2.5 2007/02/04 06:17:14 ariff Exp $
   27  */
   28 
   29 #include <dev/sound/pcm/sound.h>
   30 #include <dev/sound/pcm/vchan.h>
   31 #include "feeder_if.h"
   32 
   33 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.8 2008/01/06 16:55:51 swildner Exp $");
   34 
   35 /*
   36  * Default speed
   37  */
   38 #define VCHAN_DEFAULT_SPEED     48000
   39 
   40 extern int feeder_rate_ratemin;
   41 extern int feeder_rate_ratemax;
   42 
   43 struct vchinfo {
   44         u_int32_t spd, fmt, blksz, bps, run;
   45         struct pcm_channel *channel, *parent;
   46         struct pcmchan_caps caps;
   47 };
   48 
   49 static u_int32_t vchan_fmt[] = {
   50         AFMT_STEREO | AFMT_S16_LE,
   51         0
   52 };
   53 
   54 /*
   55  * These values are: (1.022**v-1)*65536/(1.022**100-1)
   56  *
   57  * Inspired by inverting opensound (BSDL'd)'s mix_cvt.
   58  */
   59 static int log_mix_cvt[101] = {
   60 0, 184, 373, 565, 762, 964, 1170, 1380, 1595, 1814, 2039, 2268, 2503, 2742,
   61 2987, 3238, 3493, 3755, 4022, 4295, 4574, 4859, 5151, 5449, 5753, 6064, 6382,
   62 6707, 7039, 7379, 7726, 8080, 8442, 8813, 9191, 9578, 9973, 10377, 10790,
   63 11212, 11643, 12084, 12534, 12995, 13465, 13946, 14437, 14940, 15453, 15977,
   64 16513, 17061, 17621, 18193, 18778, 19376, 19987, 20611, 21249, 21901, 22567,
   65 23248, 23944, 24656, 25383, 26126, 26885, 27661, 28454, 29265, 30093, 30940,
   66 31805, 32689, 33593, 34517, 35461, 36425, 37411, 38419, 39449, 40501, 41577,
   67 42676, 43799, 44947, 46121, 47320, 48546, 49798, 51078, 52387, 53724, 55090,
   68 56487, 57914, 59373, 60864, 62387, 63944, 65536
   69 };
   70 
   71 static int
   72 vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count, int volume)
   73 {
   74         /*
   75          * to is the output buffer, tmp is the input buffer
   76          * count is the number of 16bit samples to mix
   77          * volume is in range 0-100, (left | (right << 8))
   78          */
   79         int i;
   80         int x;
   81         int scale[2];
   82         int doscale;
   83 
   84         scale[0] = log_mix_cvt[volume & 0xff];
   85         scale[1] = log_mix_cvt[(volume >> 8) & 0xff];
   86         doscale = volume != (100 | (100 << 8));
   87 
   88         for(i = 0; i < count; i++) {
   89                 x = to[i];
   90                 if (doscale)
   91                         x += ((int)tmp[i] * scale[i % 2]) >> 16;
   92                 else
   93                         x += tmp[i];
   94                 if (x < -32768) {
   95                         /* kprintf("%d + %d = %d (u)\n", to[i], tmp[i], x); */
   96                         x = -32768;
   97                 }
   98                 if (x > 32767) {
   99                         /* kprintf("%d + %d = %d (o)\n", to[i], tmp[i], x); */
  100                         x = 32767;
  101                 }
  102                 to[i] = x & 0x0000ffff;
  103         }
  104         return 0;
  105 }
  106 
  107 static int
  108 feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
  109 {
  110         /* we're going to abuse things a bit */
  111         struct snd_dbuf *src = source;
  112         struct pcmchan_children *cce;
  113         struct pcm_channel *ch;
  114         uint32_t sz;
  115         int16_t *tmp, *dst;
  116         unsigned int cnt, rcnt = 0;
  117 
  118         #if 0
  119         if (sndbuf_getsize(src) < count)
  120                 panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x",
  121                     c->name, sndbuf_getsize(src), count, c->flags);
  122         #endif
  123         sz = sndbuf_getsize(src);
  124         if (sz < count)
  125                 count = sz;
  126         count &= ~1;
  127         if (count < 2)
  128                 return 0;
  129         bzero(b, count);
  130 
  131         /*
  132          * we are going to use our source as a temporary buffer since it's
  133          * got no other purpose.  we obtain our data by traversing the channel
  134          * list of children and calling vchan_mix_* to mix count bytes from each
  135          * into our destination buffer, b
  136          */
  137         dst = (int16_t *)b;
  138         tmp = (int16_t *)sndbuf_getbuf(src);
  139         bzero(tmp, count);
  140         SLIST_FOREACH(cce, &c->children, link) {
  141                 ch = cce->channel;
  142                 CHN_LOCK(ch);
  143                 if (ch->flags & CHN_F_TRIGGERED) {
  144                         if (ch->flags & CHN_F_MAPPED)
  145                                 sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
  146                         cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft);
  147                         vchan_mix_s16(dst, tmp, cnt >> 1, ch->volume);
  148                         if (cnt > rcnt)
  149                                 rcnt = cnt;
  150                 }
  151                 CHN_UNLOCK(ch);
  152         }
  153 
  154         return rcnt & ~1;
  155 }
  156 
  157 static struct pcm_feederdesc feeder_vchan_s16_desc[] = {
  158         {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
  159         {0},
  160 };
  161 static kobj_method_t feeder_vchan_s16_methods[] = {
  162         KOBJMETHOD(feeder_feed,         feed_vchan_s16),
  163         KOBJMETHOD_END
  164 };
  165 FEEDER_DECLARE(feeder_vchan_s16, 2, NULL);
  166 
  167 /************************************************************/
  168 
  169 static void *
  170 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
  171 {
  172         struct vchinfo *ch;
  173         struct pcm_channel *parent = devinfo;
  174 
  175         KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction"));
  176         ch = kmalloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
  177         ch->parent = parent;
  178         ch->channel = c;
  179         ch->fmt = AFMT_U8;
  180         ch->spd = DSP_DEFAULT_SPEED;
  181         ch->blksz = 2048;
  182 
  183         c->flags |= CHN_F_VIRTUAL;
  184 
  185         return ch;
  186 }
  187 
  188 static int
  189 vchan_free(kobj_t obj, void *data)
  190 {
  191         kfree(data, M_DEVBUF);
  192         return 0;
  193 }
  194 
  195 static int
  196 vchan_setformat(kobj_t obj, void *data, u_int32_t format)
  197 {
  198         struct vchinfo *ch = data;
  199         struct pcm_channel *parent = ch->parent;
  200         struct pcm_channel *channel = ch->channel;
  201 
  202         ch->fmt = format;
  203         ch->bps = 1;
  204         ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
  205         if (ch->fmt & AFMT_16BIT)
  206                 ch->bps <<= 1;
  207         else if (ch->fmt & AFMT_24BIT)
  208                 ch->bps *= 3;
  209         else if (ch->fmt & AFMT_32BIT)
  210                 ch->bps <<= 2;
  211         CHN_UNLOCK(channel);
  212         chn_notify(parent, CHN_N_FORMAT);
  213         CHN_LOCK(channel);
  214         sndbuf_setfmt(channel->bufsoft, format);
  215         return 0;
  216 }
  217 
  218 static int
  219 vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
  220 {
  221         struct vchinfo *ch = data;
  222         struct pcm_channel *parent = ch->parent;
  223         struct pcm_channel *channel = ch->channel;
  224 
  225         ch->spd = speed;
  226         CHN_UNLOCK(channel);
  227         CHN_LOCK(parent);
  228         speed = sndbuf_getspd(parent->bufsoft);
  229         CHN_UNLOCK(parent);
  230         CHN_LOCK(channel);
  231         return speed;
  232 }
  233 
  234 static int
  235 vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
  236 {
  237         struct vchinfo *ch = data;
  238         struct pcm_channel *channel = ch->channel;
  239         struct pcm_channel *parent = ch->parent;
  240         /* struct pcm_channel *channel = ch->channel; */
  241         int prate, crate;
  242 
  243         ch->blksz = blocksize;
  244         /* CHN_UNLOCK(channel); */
  245         sndbuf_setblksz(channel->bufhard, blocksize);
  246         chn_notify(parent, CHN_N_BLOCKSIZE);
  247         CHN_LOCK(parent);
  248         /* CHN_LOCK(channel); */
  249 
  250         crate = ch->spd * ch->bps;
  251         prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft);
  252         blocksize = sndbuf_getblksz(parent->bufsoft);
  253         CHN_UNLOCK(parent);
  254         blocksize *= prate;
  255         blocksize /= crate;
  256 
  257         return blocksize;
  258 }
  259 
  260 static int
  261 vchan_trigger(kobj_t obj, void *data, int go)
  262 {
  263         struct vchinfo *ch = data;
  264         struct pcm_channel *parent = ch->parent;
  265         struct pcm_channel *channel = ch->channel;
  266 
  267         if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
  268                 return 0;
  269 
  270         ch->run = (go == PCMTRIG_START)? 1 : 0;
  271         CHN_UNLOCK(channel);
  272         chn_notify(parent, CHN_N_TRIGGER);
  273         CHN_LOCK(channel);
  274 
  275         return 0;
  276 }
  277 
  278 static struct pcmchan_caps *
  279 vchan_getcaps(kobj_t obj, void *data)
  280 {
  281         struct vchinfo *ch = data;
  282 
  283         ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft);
  284         ch->caps.maxspeed = ch->caps.minspeed;
  285         ch->caps.fmtlist = vchan_fmt;
  286         ch->caps.caps = 0;
  287 
  288         return &ch->caps;
  289 }
  290 
  291 static kobj_method_t vchan_methods[] = {
  292         KOBJMETHOD(channel_init,                vchan_init),
  293         KOBJMETHOD(channel_free,                vchan_free),
  294         KOBJMETHOD(channel_setformat,           vchan_setformat),
  295         KOBJMETHOD(channel_setspeed,            vchan_setspeed),
  296         KOBJMETHOD(channel_setblocksize,        vchan_setblocksize),
  297         KOBJMETHOD(channel_trigger,             vchan_trigger),
  298         KOBJMETHOD(channel_getcaps,             vchan_getcaps),
  299         KOBJMETHOD_END
  300 };
  301 CHANNEL_DECLARE(vchan);
  302 
  303 #if 0
  304 /* 
  305  * On the fly vchan rate settings
  306  */
  307 #ifdef SND_DYNSYSCTL
  308 static int
  309 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
  310 {
  311         struct snddev_info *d;
  312         struct snddev_channel *sce;
  313         struct pcm_channel *c, *ch = NULL, *fake;
  314         struct pcmchan_caps *caps;
  315         int err = 0;
  316         int newspd = 0;
  317 
  318         d = oidp->oid_arg1;
  319         if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
  320                 return EINVAL;
  321         if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
  322                 pcm_inprog(d, -1);
  323                 return EINPROGRESS;
  324         }
  325         SLIST_FOREACH(sce, &d->channels, link) {
  326                 c = sce->channel;
  327                 CHN_LOCK(c);
  328                 if (c->direction == PCMDIR_PLAY) {
  329                         if (c->flags & CHN_F_VIRTUAL) {
  330                                 /* Sanity check */
  331                                 if (ch != NULL && ch != c->parentchannel) {
  332                                         CHN_UNLOCK(c);
  333                                         pcm_inprog(d, -1);
  334                                         return EINVAL;
  335                                 }
  336                                 if (req->newptr != NULL &&
  337                                                 (c->flags & CHN_F_BUSY)) {
  338                                         CHN_UNLOCK(c);
  339                                         pcm_inprog(d, -1);
  340                                         return EBUSY;
  341                                 }
  342                         } else if (c->flags & CHN_F_HAS_VCHAN) {
  343                                 /* No way!! */
  344                                 if (ch != NULL) {
  345                                         CHN_UNLOCK(c);
  346                                         pcm_inprog(d, -1);
  347                                         return EINVAL;
  348                                 }
  349                                 ch = c;
  350                                 newspd = ch->speed;
  351                         }
  352                 }
  353                 CHN_UNLOCK(c);
  354         }
  355         if (ch == NULL) {
  356                 pcm_inprog(d, -1);
  357                 return EINVAL;
  358         }
  359         err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
  360         if (err == 0 && req->newptr != NULL) {
  361                 if (newspd < 1 || newspd < feeder_rate_ratemin ||
  362                                 newspd > feeder_rate_ratemax) {
  363                         pcm_inprog(d, -1);
  364                         return EINVAL;
  365                 }
  366                 CHN_LOCK(ch);
  367                 caps = chn_getcaps(ch);
  368                 if (caps == NULL || newspd < caps->minspeed ||
  369                                 newspd > caps->maxspeed) {
  370                         CHN_UNLOCK(ch);
  371                         pcm_inprog(d, -1);
  372                         return EINVAL;
  373                 }
  374                 if (newspd != ch->speed) {
  375                         err = chn_setspeed(ch, newspd);
  376                         /*
  377                          * Try to avoid FEEDER_RATE on parent channel if the
  378                          * requested value is not supported by the hardware.
  379                          */
  380                         if (!err && (ch->feederflags & (1 << FEEDER_RATE))) {
  381                                 newspd = sndbuf_getspd(ch->bufhard);
  382                                 err = chn_setspeed(ch, newspd);
  383                         }
  384                         CHN_UNLOCK(ch);
  385                         if (err == 0) {
  386                                 fake = pcm_getfakechan(d);
  387                                 if (fake != NULL) {
  388                                         CHN_LOCK(fake);
  389                                         fake->speed = newspd;
  390                                         CHN_UNLOCK(fake);
  391                                 }
  392                         }
  393                 } else
  394                         CHN_UNLOCK(ch);
  395         }
  396         pcm_inprog(d, -1);
  397         return err;
  398 }
  399 #endif
  400 #endif
  401 
  402 /* virtual channel interface */
  403 
  404 int
  405 vchan_create(struct pcm_channel *parent)
  406 {
  407         struct snddev_info *d = parent->parentsnddev;
  408         struct pcmchan_children *pce;
  409         struct pcm_channel *child, *fake;
  410         struct pcmchan_caps *parent_caps;
  411         int err, first, speed = 0;
  412 
  413         if (!(parent->flags & CHN_F_BUSY))
  414                 return EBUSY;
  415 
  416 
  417         CHN_UNLOCK(parent);
  418 
  419         pce = kmalloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
  420 
  421         /* create a new playback channel */
  422         child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
  423         if (!child) {
  424                 kfree(pce, M_DEVBUF);
  425                 CHN_LOCK(parent);
  426                 return ENODEV;
  427         }
  428         pce->channel = child;
  429 
  430         /* add us to our grandparent's channel list */
  431         /*
  432          * XXX maybe we shouldn't always add the dev_t
  433          */
  434         err = pcm_chn_add(d, child);
  435         if (err) {
  436                 pcm_chn_destroy(child);
  437                 kfree(pce, M_DEVBUF);
  438                 CHN_LOCK(parent);
  439                 return err;
  440         }
  441 
  442         CHN_LOCK(parent);
  443         /* add us to our parent channel's children */
  444         first = SLIST_EMPTY(&parent->children);
  445         SLIST_INSERT_HEAD(&parent->children, pce, link);
  446         parent->flags |= CHN_F_HAS_VCHAN;
  447 
  448         if (first) {
  449                 parent_caps = chn_getcaps(parent);
  450                 if (parent_caps == NULL)
  451                         err = EINVAL;
  452 
  453                 if (!err)
  454                         err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
  455 
  456                 if (!err) {
  457                         fake = pcm_getfakechan(d);
  458                         if (fake != NULL) {
  459                                 /*
  460                                  * Avoid querying kernel hint, use saved value
  461                                  * from fake channel.
  462                                  */
  463                                 CHN_UNLOCK(parent);
  464                                 CHN_LOCK(fake);
  465                                 speed = fake->speed;
  466                                 CHN_UNLOCK(fake);
  467                                 CHN_LOCK(parent);
  468                         }
  469 
  470                         /*
  471                          * This is very sad. Few soundcards advertised as being
  472                          * able to do (insanely) higher/lower speed, but in
  473                          * reality, they simply can't. At least, we give user chance
  474                          * to set sane value via kernel hints or sysctl.
  475                          */
  476                         if (speed < 1) {
  477                                 int r;
  478                                 CHN_UNLOCK(parent);
  479                                 r = resource_int_value(device_get_name(parent->dev),
  480                                                         device_get_unit(parent->dev),
  481                                                                 "vchanrate", &speed);
  482                                 CHN_LOCK(parent);
  483                                 if (r != 0) {
  484                                         /*
  485                                          * Workaround for sb16 running
  486                                          * poorly at 45k / 49k.
  487                                          */
  488                                         switch (parent_caps->maxspeed) {
  489                                         case 45000:
  490                                         case 49000:
  491                                                 speed = 44100;
  492                                                 break;
  493                                         default:
  494                                                 speed = VCHAN_DEFAULT_SPEED;
  495                                                 break;
  496                                         }
  497                                 }
  498                         }
  499 
  500                         /*
  501                          * Limit speed based on driver caps.
  502                          * This is supposed to help fixed rate, non-VRA
  503                          * AC97 cards, but.. (see below)
  504                          */
  505                         if (speed < parent_caps->minspeed)
  506                                 speed = parent_caps->minspeed;
  507                         if (speed > parent_caps->maxspeed)
  508                                 speed = parent_caps->maxspeed;
  509 
  510                         /*
  511                          * We still need to limit the speed between
  512                          * feeder_rate_ratemin <-> feeder_rate_ratemax. This is
  513                          * just an escape goat if all of the above failed
  514                          * miserably.
  515                          */
  516                         if (speed < feeder_rate_ratemin)
  517                                 speed = feeder_rate_ratemin;
  518                         if (speed > feeder_rate_ratemax)
  519                                 speed = feeder_rate_ratemax;
  520 
  521                         err = chn_setspeed(parent, speed);
  522                         /*
  523                          * Try to avoid FEEDER_RATE on parent channel if the
  524                          * requested value is not supported by the hardware.
  525                          */
  526                         if (!err && (parent->feederflags & (1 << FEEDER_RATE))) {
  527                                 speed = sndbuf_getspd(parent->bufhard);
  528                                 err = chn_setspeed(parent, speed);
  529                         }
  530 
  531                         if (!err && fake != NULL) {
  532                                 /*
  533                                  * Save new value to fake channel.
  534                                  */
  535                                 CHN_UNLOCK(parent);
  536                                 CHN_LOCK(fake);
  537                                 fake->speed = speed;
  538                                 CHN_UNLOCK(fake);
  539                                 CHN_LOCK(parent);
  540                         }
  541                 }
  542                 
  543                 if (err) {
  544                         SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
  545                         parent->flags &= ~CHN_F_HAS_VCHAN;
  546                         CHN_UNLOCK(parent);
  547                         kfree(pce, M_DEVBUF);
  548                         if (pcm_chn_remove(d, child) == 0)
  549                                 pcm_chn_destroy(child);
  550                         CHN_LOCK(parent);
  551                         return err;
  552                 }
  553         }
  554 
  555         return 0;
  556 }
  557 
  558 int
  559 vchan_destroy(struct pcm_channel *c)
  560 {
  561         struct pcm_channel *parent = c->parentchannel;
  562         struct snddev_info *d = parent->parentsnddev;
  563         struct pcmchan_children *pce;
  564         struct snddev_channel *sce;
  565         uint32_t spd;
  566         int err;
  567 
  568         CHN_LOCK(parent);
  569         if (!(parent->flags & CHN_F_BUSY)) {
  570                 CHN_UNLOCK(parent);
  571                 return EBUSY;
  572         }
  573         if (SLIST_EMPTY(&parent->children)) {
  574                 CHN_UNLOCK(parent);
  575                 return EINVAL;
  576         }
  577 
  578         /* remove us from our parent's children list */
  579         SLIST_FOREACH(pce, &parent->children, link) {
  580                 if (pce->channel == c)
  581                         goto gotch;
  582         }
  583         CHN_UNLOCK(parent);
  584         return EINVAL;
  585 gotch:
  586         SLIST_FOREACH(sce, &d->channels, link) {
  587                 if (sce->channel == c) {
  588                         if (sce->dsp_dev) {
  589                                 destroy_dev(sce->dsp_dev);
  590                                 sce->dsp_dev = NULL;
  591                         }
  592                         d->devcount--;
  593                         break;
  594                 }
  595         }
  596         SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
  597         kfree(pce, M_DEVBUF);
  598 
  599         if (SLIST_EMPTY(&parent->children)) {
  600                 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
  601                 spd = parent->speed;
  602                 if (chn_reset(parent, parent->format) == 0)
  603                         chn_setspeed(parent, spd);
  604         }
  605 
  606         /* remove us from our grandparent's channel list */
  607         err = pcm_chn_remove(d, c);
  608 
  609         CHN_UNLOCK(parent);
  610         /* destroy ourselves */
  611         if (!err)
  612                 err = pcm_chn_destroy(c);
  613 
  614         return err;
  615 }
  616 
  617 int
  618 vchan_initsys(device_t dev)
  619 {
  620 #ifdef SND_DYNSYSCTL
  621         struct snddev_info *d;
  622 
  623         d = device_get_softc(dev);
  624         SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
  625             OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
  626             sysctl_hw_snd_vchans, "I", "");
  627 #if 0
  628         SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
  629             OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
  630             sysctl_hw_snd_vchanrate, "I", "");
  631 #endif
  632 #endif
  633 
  634         return 0;
  635 }

Cache object: dee2e694d1d400700fbb57fca06e23c2


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