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: releng/5.0/sys/dev/sound/pcm/sound.c 107285 2002-11-26 18:16:27Z cg $");
   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         struct mtx *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                                         break;          /* restart */
  284                                 }
  285                         }
  286                 }
  287         }
  288 }
  289 
  290 #ifdef USING_DEVFS
  291 static int
  292 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
  293 {
  294         struct snddev_info *d;
  295         int error, unit;
  296 
  297         unit = snd_unit;
  298         error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
  299         if (error == 0 && req->newptr != NULL) {
  300                 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
  301                         return EINVAL;
  302                 d = devclass_get_softc(pcm_devclass, unit);
  303                 if (d == NULL || SLIST_EMPTY(&d->channels))
  304                         return EINVAL;
  305                 snd_unit = unit;
  306         }
  307         return (error);
  308 }
  309 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
  310             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
  311 #endif
  312 
  313 static int
  314 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
  315 {
  316         struct snddev_info *d;
  317         int i, v, error;
  318 
  319         v = snd_maxautovchans;
  320         error = sysctl_handle_int(oidp, &v, sizeof(v), req);
  321         if (error == 0 && req->newptr != NULL) {
  322                 if (v < 0 || v >= SND_MAXVCHANS)
  323                         return EINVAL;
  324                 if (v != snd_maxautovchans) {
  325                         for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
  326                                 d = devclass_get_softc(pcm_devclass, i);
  327                                 if (!d)
  328                                         continue;
  329                                 pcm_setmaxautovchans(d, v);
  330                         }
  331                 }
  332                 snd_maxautovchans = v;
  333         }
  334         return (error);
  335 }
  336 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
  337             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
  338 
  339 struct pcm_channel *
  340 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
  341 {
  342         struct pcm_channel *ch;
  343         char *dirs;
  344         int err, *pnum;
  345 
  346         switch(dir) {
  347         case PCMDIR_PLAY:
  348                 dirs = "play";
  349                 pnum = &d->playcount;
  350                 break;
  351 
  352         case PCMDIR_REC:
  353                 dirs = "record";
  354                 pnum = &d->reccount;
  355                 break;
  356 
  357         case PCMDIR_VIRTUAL:
  358                 dirs = "virtual";
  359                 dir = PCMDIR_PLAY;
  360                 pnum = &d->vchancount;
  361                 break;
  362 
  363         default:
  364                 return NULL;
  365         }
  366 
  367         ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
  368         if (!ch)
  369                 return NULL;
  370 
  371         ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
  372         if (!ch->methods) {
  373                 free(ch, M_DEVBUF);
  374 
  375                 return NULL;
  376         }
  377 
  378         snd_mtxlock(d->lock);
  379         ch->num = (*pnum)++;
  380         snd_mtxunlock(d->lock);
  381 
  382         ch->pid = -1;
  383         ch->parentsnddev = d;
  384         ch->parentchannel = parent;
  385         ch->dev = d->dev;
  386         snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
  387 
  388         err = chn_init(ch, devinfo, dir);
  389         if (err) {
  390                 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
  391                 kobj_delete(ch->methods, M_DEVBUF);
  392                 free(ch, M_DEVBUF);
  393                 snd_mtxlock(d->lock);
  394                 (*pnum)--;
  395                 snd_mtxunlock(d->lock);
  396 
  397                 return NULL;
  398         }
  399 
  400         return ch;
  401 }
  402 
  403 int
  404 pcm_chn_destroy(struct pcm_channel *ch)
  405 {
  406         struct snddev_info *d;
  407         int err;
  408 
  409         d = ch->parentsnddev;
  410         err = chn_kill(ch);
  411         if (err) {
  412                 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
  413                 return err;
  414         }
  415 
  416         kobj_delete(ch->methods, M_DEVBUF);
  417         free(ch, M_DEVBUF);
  418 
  419         return 0;
  420 }
  421 
  422 int
  423 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
  424 {
  425         struct snddev_channel *sce, *tmp, *after;
  426         int unit = device_get_unit(d->dev);
  427         int x = -1;
  428 
  429         sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
  430         if (!sce) {
  431                 return ENOMEM;
  432         }
  433 
  434         snd_mtxlock(d->lock);
  435         sce->channel = ch;
  436         if (SLIST_EMPTY(&d->channels)) {
  437                 SLIST_INSERT_HEAD(&d->channels, sce, link);
  438         } else {
  439                 after = NULL;
  440                 SLIST_FOREACH(tmp, &d->channels, link) {
  441                         after = tmp;
  442                 }
  443                 SLIST_INSERT_AFTER(after, sce, link);
  444         }
  445         if (mkdev)
  446                 x = d->devcount++;
  447         snd_mtxunlock(d->lock);
  448 
  449         if (mkdev) {
  450                 dsp_register(unit, x);
  451                 if (ch->direction == PCMDIR_REC)
  452                         dsp_registerrec(unit, ch->num);
  453         }
  454 
  455         return 0;
  456 }
  457 
  458 int
  459 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
  460 {
  461         struct snddev_channel *sce;
  462         int unit = device_get_unit(d->dev);
  463 
  464         snd_mtxlock(d->lock);
  465         SLIST_FOREACH(sce, &d->channels, link) {
  466                 if (sce->channel == ch)
  467                         goto gotit;
  468         }
  469         snd_mtxunlock(d->lock);
  470         return EINVAL;
  471 gotit:
  472         SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
  473         if (rmdev) {
  474                 dsp_unregister(unit, --d->devcount);
  475                 if (ch->direction == PCMDIR_REC)
  476                         dsp_unregisterrec(unit, ch->num);
  477         }
  478 
  479         if (ch->direction == PCMDIR_REC)
  480                 d->reccount--;
  481         else if (ch->flags & CHN_F_VIRTUAL)
  482                 d->vchancount--;
  483         else
  484                 d->playcount--;
  485 
  486         snd_mtxunlock(d->lock);
  487         free(sce, M_DEVBUF);
  488 
  489         return 0;
  490 }
  491 
  492 int
  493 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
  494 {
  495         struct snddev_info *d = device_get_softc(dev);
  496         struct pcm_channel *ch;
  497         int err;
  498 
  499         ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
  500         if (!ch) {
  501                 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
  502                 return ENODEV;
  503         }
  504 
  505         err = pcm_chn_add(d, ch, 1);
  506         if (err) {
  507                 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
  508                 snd_mtxunlock(d->lock);
  509                 pcm_chn_destroy(ch);
  510                 return err;
  511         }
  512 
  513         if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
  514             ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
  515                 ch->flags |= CHN_F_BUSY;
  516                 err = vchan_create(ch);
  517                 if (err) {
  518                         device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
  519                         ch->flags &= ~CHN_F_BUSY;
  520                 }
  521         }
  522 
  523         return err;
  524 }
  525 
  526 static int
  527 pcm_killchan(device_t dev)
  528 {
  529         struct snddev_info *d = device_get_softc(dev);
  530         struct snddev_channel *sce;
  531         struct pcm_channel *ch;
  532         int error = 0;
  533 
  534         snd_mtxlock(d->lock);
  535         sce = SLIST_FIRST(&d->channels);
  536         snd_mtxunlock(d->lock);
  537         ch = sce->channel;
  538 
  539         error = pcm_chn_remove(d, sce->channel, 1);
  540         if (error)
  541                 return (error);
  542         return (pcm_chn_destroy(ch));
  543 }
  544 
  545 int
  546 pcm_setstatus(device_t dev, char *str)
  547 {
  548         struct snddev_info *d = device_get_softc(dev);
  549 
  550         snd_mtxlock(d->lock);
  551         strncpy(d->status, str, SND_STATUSLEN);
  552         snd_mtxunlock(d->lock);
  553         return 0;
  554 }
  555 
  556 u_int32_t
  557 pcm_getflags(device_t dev)
  558 {
  559         struct snddev_info *d = device_get_softc(dev);
  560 
  561         return d->flags;
  562 }
  563 
  564 void
  565 pcm_setflags(device_t dev, u_int32_t val)
  566 {
  567         struct snddev_info *d = device_get_softc(dev);
  568 
  569         d->flags = val;
  570 }
  571 
  572 void *
  573 pcm_getdevinfo(device_t dev)
  574 {
  575         struct snddev_info *d = device_get_softc(dev);
  576 
  577         return d->devinfo;
  578 }
  579 
  580 unsigned int
  581 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
  582 {
  583         struct snddev_info *d = device_get_softc(dev);
  584         int sz, x;
  585 
  586         sz = 0;
  587         if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
  588                 x = sz;
  589                 RANGE(sz, min, max);
  590                 if (x != sz)
  591                         device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
  592                 x = min;
  593                 while (x < sz)
  594                         x <<= 1;
  595                 if (x > sz)
  596                         x >>= 1;
  597                 if (x != sz) {
  598                         device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
  599                         sz = x;
  600                 }
  601         } else {
  602                 sz = deflt;
  603         }
  604 
  605         d->bufsz = sz;
  606 
  607         return sz;
  608 }
  609 
  610 int
  611 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
  612 {
  613         struct snddev_info *d = device_get_softc(dev);
  614 
  615         if (pcm_veto_load) {
  616                 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
  617 
  618                 return EINVAL;
  619         }
  620 
  621         d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
  622 
  623         d->flags = 0;
  624         d->dev = dev;
  625         d->devinfo = devinfo;
  626         d->devcount = 0;
  627         d->reccount = 0;
  628         d->playcount = 0;
  629         d->vchancount = 0;
  630         d->inprog = 0;
  631 
  632         if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
  633                 d->flags |= SD_F_SIMPLEX;
  634 
  635         d->fakechan = fkchan_setup(dev);
  636         chn_init(d->fakechan, NULL, 0);
  637 
  638 #ifdef SND_DYNSYSCTL
  639         sysctl_ctx_init(&d->sysctl_tree);
  640         d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
  641                                  SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
  642                                  device_get_nameunit(dev), CTLFLAG_RD, 0, "");
  643         if (d->sysctl_tree_top == NULL) {
  644                 sysctl_ctx_free(&d->sysctl_tree);
  645                 goto no;
  646         }
  647         SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
  648             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
  649 #endif
  650         if (numplay > 0)
  651                 vchan_initsys(dev);
  652         if (numplay == 1)
  653                 d->flags |= SD_F_AUTOVCHAN;
  654 
  655         sndstat_register(dev, d->status, sndstat_prepare_pcm);
  656         return 0;
  657 no:
  658         snd_mtxfree(d->lock);
  659         return ENXIO;
  660 }
  661 
  662 int
  663 pcm_unregister(device_t dev)
  664 {
  665         struct snddev_info *d = device_get_softc(dev);
  666         struct snddev_channel *sce;
  667         struct pcm_channel *ch;
  668 
  669         snd_mtxlock(d->lock);
  670         if (d->inprog) {
  671                 device_printf(dev, "unregister: operation in progress\n");
  672                 snd_mtxunlock(d->lock);
  673                 return EBUSY;
  674         }
  675         if (sndstat_busy() != 0) {
  676                 device_printf(dev, "unregister: sndstat busy\n");
  677                 snd_mtxunlock(d->lock);
  678                 return EBUSY;
  679         }
  680         SLIST_FOREACH(sce, &d->channels, link) {
  681                 ch = sce->channel;
  682                 if (ch->refcount > 0) {
  683                         device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
  684                         snd_mtxunlock(d->lock);
  685                         return EBUSY;
  686                 }
  687         }
  688         if (mixer_uninit(dev)) {
  689                 device_printf(dev, "unregister: mixer busy\n");
  690                 snd_mtxunlock(d->lock);
  691                 return EBUSY;
  692         }
  693 
  694 #ifdef SND_DYNSYSCTL
  695         d->sysctl_tree_top = NULL;
  696         sysctl_ctx_free(&d->sysctl_tree);
  697 #endif
  698         while (!SLIST_EMPTY(&d->channels))
  699                 pcm_killchan(dev);
  700 
  701         chn_kill(d->fakechan);
  702         fkchan_kill(d->fakechan);
  703 
  704         sndstat_unregister(dev);
  705         snd_mtxfree(d->lock);
  706         return 0;
  707 }
  708 
  709 /************************************************************************/
  710 
  711 static int
  712 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
  713 {
  714         struct snddev_info *d;
  715         struct snddev_channel *sce;
  716         struct pcm_channel *c;
  717         struct pcm_feeder *f;
  718         int pc, rc, vc;
  719 
  720         if (verbose < 1)
  721                 return 0;
  722 
  723         d = device_get_softc(dev);
  724         if (!d)
  725                 return ENXIO;
  726 
  727         snd_mtxlock(d->lock);
  728         if (!SLIST_EMPTY(&d->channels)) {
  729                 pc = rc = vc = 0;
  730                 SLIST_FOREACH(sce, &d->channels, link) {
  731                         c = sce->channel;
  732                         if (c->direction == PCMDIR_PLAY) {
  733                                 if (c->flags & CHN_F_VIRTUAL)
  734                                         vc++;
  735                                 else
  736                                         pc++;
  737                         } else
  738                                 rc++;
  739                 }
  740                 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
  741                                 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
  742 #ifdef USING_DEVFS
  743                                 (device_get_unit(dev) == snd_unit)? " default" : ""
  744 #else
  745                                 ""
  746 #endif
  747                                 );
  748                 if (verbose <= 1)
  749                         goto skipverbose;
  750                 SLIST_FOREACH(sce, &d->channels, link) {
  751                         c = sce->channel;
  752                         sbuf_printf(s, "\n\t");
  753 
  754                         sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
  755                         sbuf_printf(s, "spd %d", c->speed);
  756                         if (c->speed != sndbuf_getspd(c->bufhard))
  757                                 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
  758                         sbuf_printf(s, ", fmt 0x%08x", c->format);
  759                         if (c->format != sndbuf_getfmt(c->bufhard))
  760                                 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
  761                         sbuf_printf(s, ", flags %08x", c->flags);
  762                         if (c->pid != -1)
  763                                 sbuf_printf(s, ", pid %d", c->pid);
  764                         sbuf_printf(s, "\n\t");
  765 
  766                         if (c->bufhard != NULL && c->bufsoft != NULL) {
  767                                 sbuf_printf(s, "interrupts %d, ", c->interrupts);
  768                                 if (c->direction == PCMDIR_REC)
  769                                         sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
  770                                                 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
  771                                 else
  772                                         sbuf_printf(s, "underruns %d, ready %d",
  773                                                 c->xruns, sndbuf_getready(c->bufsoft));
  774                                 sbuf_printf(s, "\n\t");
  775                         }
  776 
  777                         sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
  778                         sbuf_printf(s, " -> ");
  779                         f = c->feeder;
  780                         while (f->source != NULL)
  781                                 f = f->source;
  782                         while (f != NULL) {
  783                                 sbuf_printf(s, "%s", f->class->name);
  784                                 if (f->desc->type == FEEDER_FMT)
  785                                         sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
  786                                 if (f->desc->type == FEEDER_RATE)
  787                                         sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
  788                                 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
  789                                         sbuf_printf(s, "(0x%08x)", f->desc->out);
  790                                 sbuf_printf(s, " -> ");
  791                                 f = f->parent;
  792                         }
  793                         sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
  794                 }
  795         } else
  796                 sbuf_printf(s, " (mixer only)");
  797 skipverbose:
  798         snd_mtxunlock(d->lock);
  799 
  800         return 0;
  801 }
  802 
  803 /************************************************************************/
  804 
  805 #ifdef SND_DYNSYSCTL
  806 int
  807 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
  808 {
  809         struct snddev_info *d;
  810         struct snddev_channel *sce;
  811         struct pcm_channel *c;
  812         int err, newcnt, cnt;
  813 
  814         d = oidp->oid_arg1;
  815 
  816         pcm_lock(d);
  817         cnt = 0;
  818         SLIST_FOREACH(sce, &d->channels, link) {
  819                 c = sce->channel;
  820                 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
  821                         cnt++;
  822         }
  823         newcnt = cnt;
  824 
  825         pcm_unlock(d);
  826         err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
  827         pcm_lock(d);
  828         /*
  829          * Since we dropped the pcm_lock, reload cnt now as it may
  830          * have changed.
  831          */
  832         cnt = 0;
  833         SLIST_FOREACH(sce, &d->channels, link) {
  834                 c = sce->channel;
  835                 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
  836                         cnt++;
  837         }
  838         if (err == 0 && req->newptr != NULL) {
  839                 if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
  840                         pcm_unlock(d);
  841                         return EINVAL;
  842                 }
  843 
  844                 if (newcnt > cnt) {
  845                         /* add new vchans - find a parent channel first */
  846                         SLIST_FOREACH(sce, &d->channels, link) {
  847                                 c = sce->channel;
  848                                 /* not a candidate if not a play channel */
  849                                 if (c->direction != PCMDIR_PLAY)
  850                                         continue;
  851                                 /* not a candidate if a virtual channel */
  852                                 if (c->flags & CHN_F_VIRTUAL)
  853                                         continue;
  854                                 /* not a candidate if it's in use */
  855                                 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
  856                                         continue;
  857                                 /*
  858                                  * if we get here we're a nonvirtual play channel, and either
  859                                  * 1) not busy
  860                                  * 2) busy with children, not directly open
  861                                  *
  862                                  * thus we can add children
  863                                  */
  864                                 goto addok;
  865                         }
  866                         pcm_unlock(d);
  867                         return EBUSY;
  868 addok:
  869                         c->flags |= CHN_F_BUSY;
  870                         while (err == 0 && newcnt > cnt) {
  871                                 err = vchan_create(c);
  872                                 if (err == 0)
  873                                         cnt++;
  874                         }
  875                         if (SLIST_EMPTY(&c->children))
  876                                 c->flags &= ~CHN_F_BUSY;
  877                 } else if (newcnt < cnt) {
  878                         while (err == 0 && newcnt < cnt) {
  879                                 SLIST_FOREACH(sce, &d->channels, link) {
  880                                         c = sce->channel;
  881                                         if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
  882                                                 goto remok;
  883                                 }
  884                                 pcm_unlock(d);
  885                                 return EINVAL;
  886 remok:
  887                                 err = vchan_destroy(c);
  888                                 if (err == 0)
  889                                         cnt--;
  890                         }
  891                 }
  892         }
  893 
  894         pcm_unlock(d);
  895         return err;
  896 }
  897 #endif
  898 
  899 /************************************************************************/
  900 
  901 static moduledata_t sndpcm_mod = {
  902         "snd_pcm",
  903         NULL,
  904         NULL
  905 };
  906 DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
  907 MODULE_VERSION(snd_pcm, PCM_MODVER);

Cache object: 2c551d6e4494da2317e7d6d0c1f9fb34


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