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

Cache object: e7164561e9fcfb0ba6af899f2974c137


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