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

Cache object: 356f98ab2a8266077b47b3970a947979


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