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/sound.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  * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
    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 #include <dev/sound/pcm/vchan.h>
   30 #include <sys/sysctl.h>
   31 
   32 #include "feeder_if.h"
   33 
   34 SND_DECLARE_FILE("$FreeBSD$");
   35 
   36 struct snddev_channel {
   37         SLIST_ENTRY(snddev_channel) link;
   38         struct pcm_channel *channel;
   39 };
   40 
   41 struct snddev_info {
   42         SLIST_HEAD(, snddev_channel) channels;
   43         struct pcm_channel *fakechan;
   44         unsigned devcount, playcount, reccount, vchancount;
   45         unsigned flags;
   46         int inprog;
   47         unsigned int bufsz;
   48         void *devinfo;
   49         device_t dev;
   50         char status[SND_STATUSLEN];
   51         struct sysctl_ctx_list sysctl_tree;
   52         struct sysctl_oid *sysctl_tree_top;
   53         void *lock;
   54 };
   55 
   56 devclass_t pcm_devclass;
   57 
   58 int pcm_veto_load = 1;
   59 
   60 #ifdef USING_DEVFS
   61 int snd_unit = 0;
   62 TUNABLE_INT("hw.snd.unit", &snd_unit);
   63 #endif
   64 
   65 int snd_maxautovchans = 0;
   66 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
   67 
   68 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
   69 
   70 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
   71 
   72 struct sysctl_ctx_list *
   73 snd_sysctl_tree(device_t dev)
   74 {
   75         struct snddev_info *d = device_get_softc(dev);
   76 
   77         return &d->sysctl_tree;
   78 }
   79 
   80 struct sysctl_oid *
   81 snd_sysctl_tree_top(device_t dev)
   82 {
   83         struct snddev_info *d = device_get_softc(dev);
   84 
   85         return d->sysctl_tree_top;
   86 }
   87 
   88 void *
   89 snd_mtxcreate(const char *desc, const char *type)
   90 {
   91 #ifdef USING_MUTEX
   92         struct mtx *m;
   93 
   94         m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
   95         if (m == NULL)
   96                 return NULL;
   97         mtx_init(m, desc, type, MTX_RECURSE);
   98         return m;
   99 #else
  100         return (void *)0xcafebabe;
  101 #endif
  102 }
  103 
  104 void
  105 snd_mtxfree(void *m)
  106 {
  107 #ifdef USING_MUTEX
  108         struct mtx *mtx = m;
  109 
  110         mtx_assert(mtx, MA_OWNED);
  111         mtx_destroy(mtx);
  112         free(mtx, M_DEVBUF);
  113 #endif
  114 }
  115 
  116 void
  117 snd_mtxassert(void *m)
  118 {
  119 #ifdef USING_MUTEX
  120 #ifdef INVARIANTS
  121         struct mtx *mtx = m;
  122 
  123         mtx_assert(mtx, MA_OWNED);
  124 #endif
  125 #endif
  126 }
  127 
  128 void
  129 snd_mtxlock(void *m)
  130 {
  131 #ifdef USING_MUTEX
  132         struct mtx *mtx = m;
  133 
  134         mtx_lock(mtx);
  135 #endif
  136 }
  137 
  138 void
  139 snd_mtxunlock(void *m)
  140 {
  141 #ifdef USING_MUTEX
  142         struct mtx *mtx = m;
  143 
  144         mtx_unlock(mtx);
  145 #endif
  146 }
  147 
  148 int
  149 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
  150 {
  151 #ifdef USING_MUTEX
  152         flags &= INTR_MPSAFE;
  153         flags |= INTR_TYPE_AV;
  154 #else
  155         flags = INTR_TYPE_AV;
  156 #endif
  157         return bus_setup_intr(dev, res, flags, hand, param, cookiep);
  158 }
  159 
  160 void
  161 pcm_lock(struct snddev_info *d)
  162 {
  163         snd_mtxlock(d->lock);
  164 }
  165 
  166 void
  167 pcm_unlock(struct snddev_info *d)
  168 {
  169         snd_mtxunlock(d->lock);
  170 }
  171 
  172 struct pcm_channel *
  173 pcm_getfakechan(struct snddev_info *d)
  174 {
  175         return d->fakechan;
  176 }
  177 
  178 /* return a locked channel */
  179 struct pcm_channel *
  180 pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
  181 {
  182         struct pcm_channel *c;
  183         struct snddev_channel *sce;
  184         int err;
  185 
  186         snd_mtxassert(d->lock);
  187 
  188         /* scan for a free channel */
  189         SLIST_FOREACH(sce, &d->channels, link) {
  190                 c = sce->channel;
  191                 CHN_LOCK(c);
  192                 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
  193                         if (chnum == -1 || c->num == chnum) {
  194                                 c->flags |= CHN_F_BUSY;
  195                                 c->pid = pid;
  196                                 return c;
  197                         }
  198                 }
  199                 CHN_UNLOCK(c);
  200         }
  201 
  202         /* no channel available */
  203         if (direction == PCMDIR_PLAY) {
  204                 if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
  205                         /* try to create a vchan */
  206                         SLIST_FOREACH(sce, &d->channels, link) {
  207                                 c = sce->channel;
  208                                 if (!SLIST_EMPTY(&c->children)) {
  209                                         err = vchan_create(c);
  210                                         if (!err)
  211                                                 return pcm_chnalloc(d, direction, pid, -1);
  212                                         else
  213                                                 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
  214                                 }
  215                         }
  216                 }
  217         }
  218 
  219         return NULL;
  220 }
  221 
  222 /* release a locked channel and unlock it */
  223 int
  224 pcm_chnrelease(struct pcm_channel *c)
  225 {
  226         CHN_LOCKASSERT(c);
  227         c->flags &= ~CHN_F_BUSY;
  228         c->pid = -1;
  229         CHN_UNLOCK(c);
  230         return 0;
  231 }
  232 
  233 int
  234 pcm_chnref(struct pcm_channel *c, int ref)
  235 {
  236         int r;
  237 
  238         CHN_LOCKASSERT(c);
  239         c->refcount += ref;
  240         r = c->refcount;
  241         return r;
  242 }
  243 
  244 int
  245 pcm_inprog(struct snddev_info *d, int delta)
  246 {
  247         d->inprog += delta;
  248         return d->inprog;
  249 }
  250 
  251 static void
  252 pcm_setmaxautovchans(struct snddev_info *d, int num)
  253 {
  254         struct pcm_channel *c;
  255         struct snddev_channel *sce;
  256         int err, done;
  257 
  258         if (num > 0 && d->vchancount == 0) {
  259                 SLIST_FOREACH(sce, &d->channels, link) {
  260                         c = sce->channel;
  261                         if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
  262                                 c->flags |= CHN_F_BUSY;
  263                                 err = vchan_create(c);
  264                                 if (err) {
  265                                         device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
  266                                         c->flags &= ~CHN_F_BUSY;
  267                                 }
  268                                 return;
  269                         }
  270                 }
  271         }
  272         if (num == 0 && d->vchancount > 0) {
  273                 done = 0;
  274                 while (!done) {
  275                         done = 1;
  276                         SLIST_FOREACH(sce, &d->channels, link) {
  277                                 c = sce->channel;
  278                                 if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
  279                                         done = 0;
  280                                         err = vchan_destroy(c);
  281                                         if (err)
  282                                                 device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
  283                                         goto restart;
  284                                 }
  285                         }
  286 restart:
  287                 }
  288         }
  289 }
  290 
  291 #ifdef USING_DEVFS
  292 static int
  293 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
  294 {
  295         struct snddev_info *d;
  296         int error, unit;
  297 
  298         unit = snd_unit;
  299         error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
  300         if (error == 0 && req->newptr != NULL) {
  301                 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
  302                         return EINVAL;
  303                 d = devclass_get_softc(pcm_devclass, unit);
  304                 if (d == NULL || SLIST_EMPTY(&d->channels))
  305                         return EINVAL;
  306                 snd_unit = unit;
  307         }
  308         return (error);
  309 }
  310 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
  311             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
  312 #endif
  313 
  314 static int
  315 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
  316 {
  317         struct snddev_info *d;
  318         int i, v, error;
  319 
  320         v = snd_maxautovchans;
  321         error = sysctl_handle_int(oidp, &v, sizeof(v), req);
  322         if (error == 0 && req->newptr != NULL) {
  323                 if (v < 0 || v >= SND_MAXVCHANS)
  324                         return EINVAL;
  325                 if (v != snd_maxautovchans) {
  326                         for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
  327                                 d = devclass_get_softc(pcm_devclass, i);
  328                                 if (!d)
  329                                         continue;
  330                                 pcm_setmaxautovchans(d, v);
  331                         }
  332                 }
  333                 snd_maxautovchans = v;
  334         }
  335         return (error);
  336 }
  337 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
  338             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
  339 
  340 struct pcm_channel *
  341 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
  342 {
  343         struct pcm_channel *ch;
  344         char *dirs;
  345         int err, *pnum;
  346 
  347         switch(dir) {
  348         case PCMDIR_PLAY:
  349                 dirs = "play";
  350                 pnum = &d->playcount;
  351                 break;
  352 
  353         case PCMDIR_REC:
  354                 dirs = "record";
  355                 pnum = &d->reccount;
  356                 break;
  357 
  358         case PCMDIR_VIRTUAL:
  359                 dirs = "virtual";
  360                 dir = PCMDIR_PLAY;
  361                 pnum = &d->vchancount;
  362                 break;
  363 
  364         default:
  365                 return NULL;
  366         }
  367 
  368         ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
  369         if (!ch)
  370                 return NULL;
  371 
  372         ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
  373         if (!ch->methods) {
  374                 free(ch, M_DEVBUF);
  375 
  376                 return NULL;
  377         }
  378 
  379         ch->num = (*pnum)++;
  380 
  381         ch->pid = -1;
  382         ch->parentsnddev = d;
  383         ch->parentchannel = parent;
  384         ch->dev = d->dev;
  385         snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num);
  386 
  387         err = chn_init(ch, devinfo, dir);
  388         if (err) {
  389                 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
  390                 kobj_delete(ch->methods, M_DEVBUF);
  391                 free(ch, M_DEVBUF);
  392                 (*pnum)--;
  393 
  394                 return NULL;
  395         }
  396 
  397         return ch;
  398 }
  399 
  400 int
  401 pcm_chn_destroy(struct pcm_channel *ch)
  402 {
  403         struct snddev_info *d;
  404         int err;
  405 
  406         d = ch->parentsnddev;
  407         err = chn_kill(ch);
  408         if (err) {
  409                 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
  410                 return err;
  411         }
  412 
  413         if (ch->direction == PCMDIR_REC)
  414                 d->reccount--;
  415         else if (ch->flags & CHN_F_VIRTUAL)
  416                 d->vchancount--;
  417         else
  418                 d->playcount--;
  419 
  420         kobj_delete(ch->methods, M_DEVBUF);
  421         free(ch, M_DEVBUF);
  422 
  423         return 0;
  424 }
  425 
  426 int
  427 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
  428 {
  429         struct snddev_channel *sce, *tmp, *after;
  430         int unit = device_get_unit(d->dev);
  431 
  432         snd_mtxlock(d->lock);
  433 
  434         sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
  435         if (!sce) {
  436                 snd_mtxunlock(d->lock);
  437                 return ENOMEM;
  438         }
  439 
  440         sce->channel = ch;
  441         if (SLIST_EMPTY(&d->channels)) {
  442                 SLIST_INSERT_HEAD(&d->channels, sce, link);
  443         } else {
  444                 after = NULL;
  445                 SLIST_FOREACH(tmp, &d->channels, link) {
  446                         after = tmp;
  447                 }
  448                 SLIST_INSERT_AFTER(after, sce, link);
  449         }
  450 
  451         if (mkdev) {
  452                 dsp_register(unit, d->devcount++);
  453                 if (ch->direction == PCMDIR_REC)
  454                         dsp_registerrec(unit, ch->num);
  455         }
  456 
  457         snd_mtxunlock(d->lock);
  458 
  459         return 0;
  460 }
  461 
  462 int
  463 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
  464 {
  465         struct snddev_channel *sce;
  466         int unit = device_get_unit(d->dev);
  467 
  468         snd_mtxlock(d->lock);
  469         SLIST_FOREACH(sce, &d->channels, link) {
  470                 if (sce->channel == ch)
  471                         goto gotit;
  472         }
  473         snd_mtxunlock(d->lock);
  474         return EINVAL;
  475 gotit:
  476         SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
  477         free(sce, M_DEVBUF);
  478 
  479         if (rmdev) {
  480                 dsp_unregister(unit, --d->devcount);
  481                 if (ch->direction == PCMDIR_REC)
  482                         dsp_unregisterrec(unit, ch->num);
  483         }
  484         snd_mtxunlock(d->lock);
  485 
  486         return 0;
  487 }
  488 
  489 int
  490 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
  491 {
  492         struct snddev_info *d = device_get_softc(dev);
  493         struct pcm_channel *ch;
  494         int err;
  495 
  496         ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
  497         if (!ch) {
  498                 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
  499                 return ENODEV;
  500         }
  501 
  502         err = pcm_chn_add(d, ch, 1);
  503         if (err) {
  504                 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
  505                 pcm_chn_destroy(ch);
  506                 return err;
  507         }
  508 
  509         if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) {
  510                 ch->flags |= CHN_F_BUSY;
  511                 err = vchan_create(ch);
  512                 if (err) {
  513                         device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
  514                         ch->flags &= ~CHN_F_BUSY;
  515                 }
  516         }
  517 
  518         return err;
  519 }
  520 
  521 static int
  522 pcm_killchan(device_t dev)
  523 {
  524         struct snddev_info *d = device_get_softc(dev);
  525         struct snddev_channel *sce;
  526         struct pcm_channel *ch;
  527         int error;
  528 
  529         snd_mtxlock(d->lock);
  530         sce = SLIST_FIRST(&d->channels);
  531         snd_mtxunlock(d->lock);
  532         ch = sce->channel;
  533 
  534         error = pcm_chn_remove(d, sce->channel, 1);
  535         if (error)
  536                 return (error);
  537         return (pcm_chn_destroy(ch));
  538 }
  539 
  540 int
  541 pcm_setstatus(device_t dev, char *str)
  542 {
  543         struct snddev_info *d = device_get_softc(dev);
  544 
  545         snd_mtxlock(d->lock);
  546         strncpy(d->status, str, SND_STATUSLEN);
  547         snd_mtxunlock(d->lock);
  548         return 0;
  549 }
  550 
  551 u_int32_t
  552 pcm_getflags(device_t dev)
  553 {
  554         struct snddev_info *d = device_get_softc(dev);
  555 
  556         return d->flags;
  557 }
  558 
  559 void
  560 pcm_setflags(device_t dev, u_int32_t val)
  561 {
  562         struct snddev_info *d = device_get_softc(dev);
  563 
  564         d->flags = val;
  565 }
  566 
  567 void *
  568 pcm_getdevinfo(device_t dev)
  569 {
  570         struct snddev_info *d = device_get_softc(dev);
  571 
  572         return d->devinfo;
  573 }
  574 
  575 unsigned int
  576 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
  577 {
  578         struct snddev_info *d = device_get_softc(dev);
  579         int sz, x;
  580 
  581         sz = 0;
  582         if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
  583                 x = sz;
  584                 RANGE(sz, min, max);
  585                 if (x != sz)
  586                         device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
  587                 x = min;
  588                 while (x < sz)
  589                         x <<= 1;
  590                 if (x > sz)
  591                         x >>= 1;
  592                 if (x != sz) {
  593                         device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
  594                         sz = x;
  595                 }
  596         } else {
  597                 sz = deflt;
  598         }
  599 
  600         d->bufsz = sz;
  601 
  602         return sz;
  603 }
  604 
  605 int
  606 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
  607 {
  608         struct snddev_info *d = device_get_softc(dev);
  609 
  610         if (pcm_veto_load) {
  611                 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
  612 
  613                 return EINVAL;
  614         }
  615 
  616         d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
  617         snd_mtxlock(d->lock);
  618 
  619         d->flags = 0;
  620         d->dev = dev;
  621         d->devinfo = devinfo;
  622         d->devcount = 0;
  623         d->reccount = 0;
  624         d->playcount = 0;
  625         d->vchancount = 0;
  626         d->inprog = 0;
  627 
  628         if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
  629                 d->flags |= SD_F_SIMPLEX;
  630 
  631         d->fakechan = fkchan_setup(dev);
  632         chn_init(d->fakechan, NULL, 0);
  633 
  634 #ifdef SND_DYNSYSCTL
  635         sysctl_ctx_init(&d->sysctl_tree);
  636         d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
  637                                  SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
  638                                  device_get_nameunit(dev), CTLFLAG_RD, 0, "");
  639         if (d->sysctl_tree_top == NULL) {
  640                 sysctl_ctx_free(&d->sysctl_tree);
  641                 goto no;
  642         }
  643         SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
  644             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
  645 #endif
  646         if (numplay > 0)
  647                 vchan_initsys(dev);
  648         if (numplay == 1)
  649                 d->flags |= SD_F_AUTOVCHAN;
  650 
  651         snd_mtxunlock(d->lock);
  652         sndstat_register(dev, d->status, sndstat_prepare_pcm);
  653         return 0;
  654 no:
  655         snd_mtxfree(d->lock);
  656         return ENXIO;
  657 }
  658 
  659 int
  660 pcm_unregister(device_t dev)
  661 {
  662         struct snddev_info *d = device_get_softc(dev);
  663         struct snddev_channel *sce;
  664         struct pcm_channel *ch;
  665 
  666         snd_mtxlock(d->lock);
  667         if (d->inprog) {
  668                 device_printf(dev, "unregister: operation in progress\n");
  669                 snd_mtxunlock(d->lock);
  670                 return EBUSY;
  671         }
  672         if (sndstat_busy() != 0) {
  673                 device_printf(dev, "unregister: sndstat busy\n");
  674                 snd_mtxunlock(d->lock);
  675                 return EBUSY;
  676         }
  677         SLIST_FOREACH(sce, &d->channels, link) {
  678                 ch = sce->channel;
  679                 if (ch->refcount > 0) {
  680                         device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
  681                         snd_mtxunlock(d->lock);
  682                         return EBUSY;
  683                 }
  684         }
  685         if (mixer_uninit(dev)) {
  686                 device_printf(dev, "unregister: mixer busy\n");
  687                 snd_mtxunlock(d->lock);
  688                 return EBUSY;
  689         }
  690 
  691 #ifdef SND_DYNSYSCTL
  692         d->sysctl_tree_top = NULL;
  693         sysctl_ctx_free(&d->sysctl_tree);
  694 #endif
  695         while (!SLIST_EMPTY(&d->channels))
  696                 pcm_killchan(dev);
  697 
  698         chn_kill(d->fakechan);
  699         fkchan_kill(d->fakechan);
  700 
  701         snd_mtxfree(d->lock);
  702         return 0;
  703 }
  704 
  705 /************************************************************************/
  706 
  707 static int
  708 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
  709 {
  710         struct snddev_info *d;
  711         struct snddev_channel *sce;
  712         struct pcm_channel *c;
  713         struct pcm_feeder *f;
  714         int pc, rc, vc;
  715 
  716         if (verbose < 1)
  717                 return 0;
  718 
  719         d = device_get_softc(dev);
  720         if (!d)
  721                 return ENXIO;
  722 
  723         snd_mtxlock(d->lock);
  724         if (!SLIST_EMPTY(&d->channels)) {
  725                 pc = rc = vc = 0;
  726                 SLIST_FOREACH(sce, &d->channels, link) {
  727                         c = sce->channel;
  728                         if (c->direction == PCMDIR_PLAY) {
  729                                 if (c->flags & CHN_F_VIRTUAL)
  730                                         vc++;
  731                                 else
  732                                         pc++;
  733                         } else
  734                                 rc++;
  735                 }
  736                 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
  737                                 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
  738 #ifdef USING_DEVFS
  739                                 (device_get_unit(dev) == snd_unit)? " default" : ""
  740 #else
  741                                 ""
  742 #endif
  743                                 );
  744                 if (verbose <= 1)
  745                         goto skipverbose;
  746                 SLIST_FOREACH(sce, &d->channels, link) {
  747                         c = sce->channel;
  748                         sbuf_printf(s, "\n\t");
  749 
  750                         sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
  751                         sbuf_printf(s, "spd %d", c->speed);
  752                         if (c->speed != sndbuf_getspd(c->bufhard))
  753                                 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
  754                         sbuf_printf(s, ", fmt 0x%08x", c->format);
  755                         if (c->format != sndbuf_getfmt(c->bufhard))
  756                                 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
  757                         sbuf_printf(s, ", flags %08x", c->flags);
  758                         if (c->pid != -1)
  759                                 sbuf_printf(s, ", pid %d", c->pid);
  760                         sbuf_printf(s, "\n\t");
  761 
  762                         if (c->bufhard != NULL && c->bufsoft != NULL) {
  763                                 sbuf_printf(s, "interrupts %d, ", c->interrupts);
  764                                 if (c->direction == PCMDIR_REC)
  765                                         sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
  766                                                 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
  767                                 else
  768                                         sbuf_printf(s, "underruns %d, ready %d",
  769                                                 c->xruns, sndbuf_getready(c->bufsoft));
  770                                 sbuf_printf(s, "\n\t");
  771                         }
  772 
  773                         sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
  774                         sbuf_printf(s, " -> ");
  775                         f = c->feeder;
  776                         while (f->source != NULL)
  777                                 f = f->source;
  778                         while (f != NULL) {
  779                                 sbuf_printf(s, "%s", f->class->name);
  780                                 if (f->desc->type == FEEDER_FMT)
  781                                         sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
  782                                 if (f->desc->type == FEEDER_RATE)
  783                                         sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
  784                                 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
  785                                         sbuf_printf(s, "(0x%08x)", f->desc->out);
  786                                 sbuf_printf(s, " -> ");
  787                                 f = f->parent;
  788                         }
  789                         sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
  790                 }
  791 skipverbose:
  792         } else
  793                 sbuf_printf(s, " (mixer only)");
  794         snd_mtxunlock(d->lock);
  795 
  796         return 0;
  797 }
  798 
  799 /************************************************************************/
  800 
  801 #ifdef SND_DYNSYSCTL
  802 int
  803 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
  804 {
  805         struct snddev_info *d;
  806         struct snddev_channel *sce;
  807         struct pcm_channel *c;
  808         int err, oldcnt, newcnt, cnt;
  809 
  810         d = oidp->oid_arg1;
  811 
  812         pcm_lock(d);
  813         cnt = 0;
  814         SLIST_FOREACH(sce, &d->channels, link) {
  815                 c = sce->channel;
  816                 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
  817                         cnt++;
  818         }
  819         oldcnt = cnt;
  820         newcnt = cnt;
  821 
  822         err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
  823         if (err == 0 && req->newptr != NULL) {
  824                 if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
  825                         pcm_unlock(d);
  826                         return EINVAL;
  827                 }
  828 
  829                 if (newcnt > cnt) {
  830                         /* add new vchans - find a parent channel first */
  831                         SLIST_FOREACH(sce, &d->channels, link) {
  832                                 c = sce->channel;
  833                                 /* not a candidate if not a play channel */
  834                                 if (c->direction != PCMDIR_PLAY)
  835                                         goto addskip;
  836                                 /* not a candidate if a virtual channel */
  837                                 if (c->flags & CHN_F_VIRTUAL)
  838                                         goto addskip;
  839                                 /* not a candidate if it's in use */
  840                                 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
  841                                         goto addskip;
  842                                 /*
  843                                  * if we get here we're a nonvirtual play channel, and either
  844                                  * 1) not busy
  845                                  * 2) busy with children, not directly open
  846                                  *
  847                                  * thus we can add children
  848                                  */
  849                                 goto addok;
  850 addskip:
  851                         }
  852                         pcm_unlock(d);
  853                         return EBUSY;
  854 addok:
  855                         c->flags |= CHN_F_BUSY;
  856                         while (err == 0 && newcnt > cnt) {
  857                                 err = vchan_create(c);
  858                                 if (err == 0)
  859                                         cnt++;
  860                         }
  861                         if (SLIST_EMPTY(&c->children))
  862                                 c->flags &= ~CHN_F_BUSY;
  863                 } else if (newcnt < cnt) {
  864                         while (err == 0 && newcnt < cnt) {
  865                                 SLIST_FOREACH(sce, &d->channels, link) {
  866                                         c = sce->channel;
  867                                         if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
  868                                                 goto remok;
  869                                 }
  870                                 pcm_unlock(d);
  871                                 return EINVAL;
  872 remok:
  873                                 err = vchan_destroy(c);
  874                                 if (err == 0)
  875                                         cnt--;
  876                         }
  877                 }
  878         }
  879 
  880         pcm_unlock(d);
  881         return err;
  882 }
  883 #endif
  884 
  885 /************************************************************************/
  886 
  887 static moduledata_t sndpcm_mod = {
  888         "snd_pcm",
  889         NULL,
  890         NULL
  891 };
  892 DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
  893 MODULE_VERSION(snd_pcm, PCM_MODVER);

Cache object: 184d82ba2efc579df3bde31ccc377655


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