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

    1 /*-
    2  * Copyright (c) 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/6.4/sys/dev/sound/pcm/sound.c 170279 2007-06-04 09:06:05Z ariff $");
   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 = 4;
   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 static int
  162 pcm_setvchans(struct snddev_info *d, int newcnt)
  163 {
  164         struct snddev_channel *sce = NULL;
  165         struct pcm_channel *c = NULL;
  166         int err = 0, vcnt, dcnt, i;
  167 
  168         pcm_inprog(d, 1);
  169                 
  170         if (!(d->flags & SD_F_AUTOVCHAN)) {
  171                 err = EINVAL;
  172                 goto setvchans_out;
  173         }
  174 
  175         vcnt = d->vchancount;
  176         dcnt = d->playcount + d->reccount;
  177 
  178         if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) {
  179                 err = E2BIG;
  180                 goto setvchans_out;
  181         }
  182 
  183         dcnt += vcnt;
  184 
  185         if (newcnt > vcnt) {
  186                 /* add new vchans - find a parent channel first */
  187                 SLIST_FOREACH(sce, &d->channels, link) {
  188                         c = sce->channel;
  189                         CHN_LOCK(c);
  190                         if (c->direction == PCMDIR_PLAY &&
  191                                         ((c->flags & CHN_F_HAS_VCHAN) ||
  192                                         (vcnt == 0 &&
  193                                         !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)))))
  194                                 goto addok;
  195                         CHN_UNLOCK(c);
  196                 }
  197                 err = EBUSY;
  198                 goto setvchans_out;
  199 addok:
  200                 c->flags |= CHN_F_BUSY;
  201                 while (err == 0 && newcnt > vcnt) {
  202                         if (dcnt > PCMMAXCHAN) {
  203                                 device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
  204                                 break;
  205                         }
  206                         err = vchan_create(c);
  207                         if (err == 0) {
  208                                 vcnt++;
  209                                 dcnt++;
  210                         } else if (err == E2BIG && newcnt > vcnt)
  211                                 device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
  212                 }
  213                 if (vcnt == 0)
  214                         c->flags &= ~CHN_F_BUSY;
  215                 CHN_UNLOCK(c);
  216         } else if (newcnt < vcnt) {
  217 #define ORPHAN_CDEVT(cdevt) \
  218         ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \
  219         (cdevt)->si_drv2 == NULL))
  220                 while (err == 0 && newcnt < vcnt) {
  221                         i = 0;
  222                         SLIST_FOREACH(sce, &d->channels, link) {
  223                                 c = sce->channel;
  224                                 CHN_LOCK(c);
  225                                 if (c->direction == PCMDIR_PLAY &&
  226                                                 (c->flags & CHN_F_VIRTUAL) &&
  227                                                 (i++ == newcnt)) {
  228                                         if (!(c->flags & CHN_F_BUSY) &&
  229                                                         ORPHAN_CDEVT(sce->dsp_devt) &&
  230                                                         ORPHAN_CDEVT(sce->dspW_devt) &&
  231                                                         ORPHAN_CDEVT(sce->audio_devt) &&
  232                                                         ORPHAN_CDEVT(sce->dspr_devt))
  233                                                 goto remok;
  234                                         /*
  235                                          * Either we're busy, or our cdev
  236                                          * has been stolen by dsp_clone().
  237                                          * Skip, and increase newcnt.
  238                                          */
  239                                         if (!(c->flags & CHN_F_BUSY))
  240                                                 device_printf(d->dev,
  241                                                         "%s: <%s> somebody steal my cdev!\n",
  242                                                         __func__, c->name);
  243                                         newcnt++;
  244                                 }
  245                                 CHN_UNLOCK(c);
  246                         }
  247                         if (vcnt != newcnt)
  248                                 err = EBUSY;
  249                         break;
  250 remok:
  251                         CHN_UNLOCK(c);
  252                         err = vchan_destroy(c);
  253                         if (err == 0)
  254                                 vcnt--;
  255                         else
  256                                 device_printf(d->dev,
  257                                         "%s: WARNING: vchan_destroy() failed!",
  258                                         __func__);
  259                 }
  260         }
  261 
  262 setvchans_out:
  263         pcm_inprog(d, -1);
  264         return err;
  265 }
  266 
  267 /* return error status and a locked channel */
  268 int
  269 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
  270                 pid_t pid, int chnum)
  271 {
  272         struct pcm_channel *c;
  273         struct snddev_channel *sce;
  274         int err;
  275 
  276 retry_chnalloc:
  277         err = ENODEV;
  278         /* scan for a free channel */
  279         SLIST_FOREACH(sce, &d->channels, link) {
  280                 c = sce->channel;
  281                 CHN_LOCK(c);
  282                 if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
  283                         if (chnum < 0 || sce->chan_num == chnum) {
  284                                 c->flags |= CHN_F_BUSY;
  285                                 c->pid = pid;
  286                                 *ch = c;
  287                                 return 0;
  288                         }
  289                 }
  290                 if (sce->chan_num == chnum) {
  291                         if (c->direction != direction)
  292                                 err = EOPNOTSUPP;
  293                         else if (c->flags & CHN_F_BUSY)
  294                                 err = EBUSY;
  295                         else
  296                                 err = EINVAL;
  297                         CHN_UNLOCK(c);
  298                         return err;
  299                 } else if (c->direction == direction && (c->flags & CHN_F_BUSY))
  300                         err = EBUSY;
  301                 CHN_UNLOCK(c);
  302         }
  303 
  304         /* no channel available */
  305         if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 &&
  306                         d->vchancount < snd_maxautovchans &&
  307                         d->devcount <= PCMMAXCHAN) {
  308                 err = pcm_setvchans(d, d->vchancount + 1);
  309                 if (err == 0) {
  310                         chnum = -2;
  311                         goto retry_chnalloc;
  312                 }
  313         }
  314 
  315         return err;
  316 }
  317 
  318 /* release a locked channel and unlock it */
  319 int
  320 pcm_chnrelease(struct pcm_channel *c)
  321 {
  322         CHN_LOCKASSERT(c);
  323         c->flags &= ~CHN_F_BUSY;
  324         c->pid = -1;
  325         CHN_UNLOCK(c);
  326         return 0;
  327 }
  328 
  329 int
  330 pcm_chnref(struct pcm_channel *c, int ref)
  331 {
  332         int r;
  333 
  334         CHN_LOCKASSERT(c);
  335         c->refcount += ref;
  336         r = c->refcount;
  337         return r;
  338 }
  339 
  340 int
  341 pcm_inprog(struct snddev_info *d, int delta)
  342 {
  343         int r;
  344 
  345         if (delta == 0)
  346                 return d->inprog;
  347 
  348         /* backtrace(); */
  349         pcm_lock(d);
  350         d->inprog += delta;
  351         r = d->inprog;
  352         pcm_unlock(d);
  353         return r;
  354 }
  355 
  356 static void
  357 pcm_setmaxautovchans(struct snddev_info *d, int num)
  358 {
  359         if (num > 0 && d->vchancount == 0)
  360                 pcm_setvchans(d, 1);
  361         else if (num == 0 && d->vchancount > 0)
  362                 pcm_setvchans(d, 0);
  363 }
  364 
  365 #ifdef USING_DEVFS
  366 static int
  367 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
  368 {
  369         struct snddev_info *d;
  370         int error, unit;
  371 
  372         unit = snd_unit;
  373         error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
  374         if (error == 0 && req->newptr != NULL) {
  375                 if (unit < 0 || (pcm_devclass != NULL &&
  376                     unit >= devclass_get_maxunit(pcm_devclass)))
  377                         return EINVAL;
  378                 d = devclass_get_softc(pcm_devclass, unit);
  379                 if (d == NULL || SLIST_EMPTY(&d->channels))
  380                         return EINVAL;
  381                 snd_unit = unit;
  382         }
  383         return (error);
  384 }
  385 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
  386             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
  387 #endif
  388 
  389 static int
  390 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
  391 {
  392         struct snddev_info *d;
  393         int i, v, error;
  394 
  395         v = snd_maxautovchans;
  396         error = sysctl_handle_int(oidp, &v, sizeof(v), req);
  397         if (error == 0 && req->newptr != NULL) {
  398                 if (v < 0 || v > PCMMAXCHAN)
  399                         return E2BIG;
  400                 if (pcm_devclass != NULL && v != snd_maxautovchans) {
  401                         for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
  402                                 d = devclass_get_softc(pcm_devclass, i);
  403                                 if (!d)
  404                                         continue;
  405                                 pcm_setmaxautovchans(d, v);
  406                         }
  407                 }
  408                 snd_maxautovchans = v;
  409         }
  410         return (error);
  411 }
  412 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
  413             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
  414 
  415 struct pcm_channel *
  416 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
  417 {
  418         struct snddev_channel *sce;
  419         struct pcm_channel *ch, *c;
  420         char *dirs;
  421         uint32_t flsearch = 0;
  422         int direction, err, rpnum, *pnum;
  423 
  424         switch(dir) {
  425         case PCMDIR_PLAY:
  426                 dirs = "play";
  427                 direction = PCMDIR_PLAY;
  428                 pnum = &d->playcount;
  429                 break;
  430 
  431         case PCMDIR_REC:
  432                 dirs = "record";
  433                 direction = PCMDIR_REC;
  434                 pnum = &d->reccount;
  435                 break;
  436 
  437         case PCMDIR_VIRTUAL:
  438                 dirs = "virtual";
  439                 direction = PCMDIR_PLAY;
  440                 pnum = &d->vchancount;
  441                 flsearch = CHN_F_VIRTUAL;
  442                 break;
  443 
  444         default:
  445                 return NULL;
  446         }
  447 
  448         ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
  449         if (!ch)
  450                 return NULL;
  451 
  452         ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
  453         if (!ch->methods) {
  454                 free(ch, M_DEVBUF);
  455 
  456                 return NULL;
  457         }
  458 
  459         snd_mtxlock(d->lock);
  460         ch->num = 0;
  461         rpnum = 0;
  462         SLIST_FOREACH(sce, &d->channels, link) {
  463                 c = sce->channel;
  464                 if (direction != c->direction ||
  465                                 (c->flags & CHN_F_VIRTUAL) != flsearch)
  466                         continue;
  467                 if (ch->num == c->num)
  468                         ch->num++;
  469                 else {
  470 #if 0
  471                         device_printf(d->dev,
  472                                 "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
  473                                 __func__, dirs, ch->num, c->num);
  474 #endif
  475                         goto retry_num_search;
  476                 }
  477                 rpnum++;
  478         }
  479         goto retry_num_search_out;
  480 retry_num_search:
  481         rpnum = 0;
  482         SLIST_FOREACH(sce, &d->channels, link) {
  483                 c = sce->channel;
  484                 if (direction != c->direction ||
  485                                 (c->flags & CHN_F_VIRTUAL) != flsearch)
  486                         continue;
  487                 if (ch->num == c->num) {
  488                         ch->num++;
  489                         goto retry_num_search;
  490                 }
  491                 rpnum++;
  492         }
  493 retry_num_search_out:
  494         if (*pnum != rpnum) {
  495                 device_printf(d->dev,
  496                         "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
  497                         __func__, dirs, *pnum, rpnum);
  498                 *pnum = rpnum;
  499         }
  500         (*pnum)++;
  501         snd_mtxunlock(d->lock);
  502 
  503         ch->pid = -1;
  504         ch->parentsnddev = d;
  505         ch->parentchannel = parent;
  506         ch->dev = d->dev;
  507         snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
  508 
  509         err = chn_init(ch, devinfo, dir, direction);
  510         if (err) {
  511                 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
  512                 kobj_delete(ch->methods, M_DEVBUF);
  513                 free(ch, M_DEVBUF);
  514                 snd_mtxlock(d->lock);
  515                 (*pnum)--;
  516                 snd_mtxunlock(d->lock);
  517 
  518                 return NULL;
  519         }
  520 
  521         return ch;
  522 }
  523 
  524 int
  525 pcm_chn_destroy(struct pcm_channel *ch)
  526 {
  527         struct snddev_info *d;
  528         int err;
  529 
  530         d = ch->parentsnddev;
  531         err = chn_kill(ch);
  532         if (err) {
  533                 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
  534                 return err;
  535         }
  536 
  537         kobj_delete(ch->methods, M_DEVBUF);
  538         free(ch, M_DEVBUF);
  539 
  540         return 0;
  541 }
  542 
  543 int
  544 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
  545 {
  546         struct snddev_channel *sce, *tmp, *after;
  547         unsigned rdevcount;
  548         int device = device_get_unit(d->dev);
  549         size_t namelen;
  550 
  551         /*
  552          * Note it's confusing nomenclature.
  553          * dev_t
  554          * device -> pcm_device
  555          * unit -> pcm_channel
  556          * channel -> snddev_channel
  557          * device_t
  558          * unit -> pcm_device
  559          */
  560 
  561         sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
  562         if (!sce) {
  563                 return ENOMEM;
  564         }
  565 
  566         snd_mtxlock(d->lock);
  567         sce->channel = ch;
  568         sce->chan_num = 0;
  569         rdevcount = 0;
  570         after = NULL;
  571         SLIST_FOREACH(tmp, &d->channels, link) {
  572                 if (sce->chan_num == tmp->chan_num)
  573                         sce->chan_num++;
  574                 else {
  575 #if 0
  576                         device_printf(d->dev,
  577                                 "%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
  578                                 __func__, sce->chan_num, tmp->chan_num);
  579 #endif
  580                         goto retry_chan_num_search;
  581                 }
  582                 after = tmp;
  583                 rdevcount++;
  584         }
  585         goto retry_chan_num_search_out;
  586 retry_chan_num_search:
  587         /*
  588          * Look for possible channel numbering collision. This may not
  589          * be optimized, but it will ensure that no collision occured.
  590          * Can be considered cheap since none of the locking/unlocking
  591          * operations involved.
  592          */
  593         rdevcount = 0;
  594         after = NULL;
  595         SLIST_FOREACH(tmp, &d->channels, link) {
  596                 if (sce->chan_num == tmp->chan_num) {
  597                         sce->chan_num++;
  598                         goto retry_chan_num_search;
  599                 }
  600                 if (sce->chan_num > tmp->chan_num)
  601                         after = tmp;
  602                 rdevcount++;
  603         }
  604 retry_chan_num_search_out:
  605         /*
  606          * Don't overflow PCMMKMINOR / PCMMAXCHAN.
  607          */
  608         if (sce->chan_num > PCMMAXCHAN) {
  609                 snd_mtxunlock(d->lock);
  610                 device_printf(d->dev,
  611                         "%s: WARNING: sce->chan_num overflow! (%d)\n",
  612                         __func__, sce->chan_num);
  613                 free(sce, M_DEVBUF);
  614                 return E2BIG;
  615         }
  616         if (d->devcount != rdevcount) {
  617                 device_printf(d->dev,
  618                         "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
  619                         __func__, d->devcount, rdevcount);
  620                 d->devcount = rdevcount;
  621         }
  622         d->devcount++;
  623         if (after == NULL) {
  624                 SLIST_INSERT_HEAD(&d->channels, sce, link);
  625         } else {
  626                 SLIST_INSERT_AFTER(after, sce, link);
  627         }
  628 #if 0
  629         if (1) {
  630                 int cnum = 0;
  631                 SLIST_FOREACH(tmp, &d->channels, link) {
  632                         if (cnum != tmp->chan_num)
  633                                 device_printf(d->dev,
  634                                         "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
  635                                         __func__, cnum, tmp->chan_num);
  636                         cnum++;
  637                 }
  638         }
  639 #endif
  640 
  641         namelen = strlen(ch->name);
  642         if ((CHN_NAMELEN - namelen) > 10) {     /* ":dspXX.YYY" */
  643                 snprintf(ch->name + namelen,
  644                         CHN_NAMELEN - namelen, ":dsp%d.%d",
  645                         device, sce->chan_num);
  646         }
  647         snd_mtxunlock(d->lock);
  648 
  649         /*
  650          * I will revisit these someday, and nuke it mercilessly..
  651          */
  652         sce->dsp_devt = make_dev(&dsp_cdevsw,
  653                         PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
  654                         UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
  655                         device, sce->chan_num);
  656 
  657         sce->dspW_devt = make_dev(&dsp_cdevsw,
  658                         PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
  659                         UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
  660                         device, sce->chan_num);
  661 
  662         sce->audio_devt = make_dev(&dsp_cdevsw,
  663                         PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
  664                         UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
  665                         device, sce->chan_num);
  666 
  667         if (ch->direction == PCMDIR_REC)
  668                 sce->dspr_devt = make_dev(&dsp_cdevsw,
  669                                 PCMMKMINOR(device, SND_DEV_DSPREC,
  670                                         sce->chan_num), UID_ROOT, GID_WHEEL,
  671                                 0666, "dspr%d.%d", device, sce->chan_num);
  672 
  673         return 0;
  674 }
  675 
  676 int
  677 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
  678 {
  679         struct snddev_channel *sce;
  680 #if 0
  681         int ourlock;
  682 
  683         ourlock = 0;
  684         if (!mtx_owned(d->lock)) {
  685                 snd_mtxlock(d->lock);
  686                 ourlock = 1;
  687         }
  688 #endif
  689 
  690         SLIST_FOREACH(sce, &d->channels, link) {
  691                 if (sce->channel == ch)
  692                         goto gotit;
  693         }
  694 #if 0
  695         if (ourlock)
  696                 snd_mtxunlock(d->lock);
  697 #endif
  698         return EINVAL;
  699 gotit:
  700         SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
  701 
  702         if (ch->flags & CHN_F_VIRTUAL)
  703                 d->vchancount--;
  704         else if (ch->direction == PCMDIR_REC)
  705                 d->reccount--;
  706         else
  707                 d->playcount--;
  708 
  709 #if 0
  710         if (ourlock)
  711                 snd_mtxunlock(d->lock);
  712 #endif
  713         free(sce, M_DEVBUF);
  714 
  715         return 0;
  716 }
  717 
  718 int
  719 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
  720 {
  721         struct snddev_info *d = device_get_softc(dev);
  722         struct pcm_channel *ch;
  723         int err;
  724 
  725         ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
  726         if (!ch) {
  727                 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
  728                 return ENODEV;
  729         }
  730 
  731         err = pcm_chn_add(d, ch);
  732         if (err) {
  733                 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
  734                 pcm_chn_destroy(ch);
  735                 return err;
  736         }
  737 
  738         return err;
  739 }
  740 
  741 static int
  742 pcm_killchan(device_t dev)
  743 {
  744         struct snddev_info *d = device_get_softc(dev);
  745         struct snddev_channel *sce;
  746         struct pcm_channel *ch;
  747         int error = 0;
  748 
  749         sce = SLIST_FIRST(&d->channels);
  750         ch = sce->channel;
  751 
  752         error = pcm_chn_remove(d, sce->channel);
  753         if (error)
  754                 return (error);
  755         return (pcm_chn_destroy(ch));
  756 }
  757 
  758 int
  759 pcm_setstatus(device_t dev, char *str)
  760 {
  761         struct snddev_info *d = device_get_softc(dev);
  762 
  763         snd_mtxlock(d->lock);
  764         strncpy(d->status, str, SND_STATUSLEN);
  765         snd_mtxunlock(d->lock);
  766         if (snd_maxautovchans > 0)
  767                 pcm_setvchans(d, 1);
  768         return 0;
  769 }
  770 
  771 uint32_t
  772 pcm_getflags(device_t dev)
  773 {
  774         struct snddev_info *d = device_get_softc(dev);
  775 
  776         return d->flags;
  777 }
  778 
  779 void
  780 pcm_setflags(device_t dev, uint32_t val)
  781 {
  782         struct snddev_info *d = device_get_softc(dev);
  783 
  784         d->flags = val;
  785 }
  786 
  787 void *
  788 pcm_getdevinfo(device_t dev)
  789 {
  790         struct snddev_info *d = device_get_softc(dev);
  791 
  792         return d->devinfo;
  793 }
  794 
  795 unsigned int
  796 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
  797 {
  798         struct snddev_info *d = device_get_softc(dev);
  799         int sz, x;
  800 
  801         sz = 0;
  802         if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
  803                 x = sz;
  804                 RANGE(sz, min, max);
  805                 if (x != sz)
  806                         device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
  807                 x = min;
  808                 while (x < sz)
  809                         x <<= 1;
  810                 if (x > sz)
  811                         x >>= 1;
  812                 if (x != sz) {
  813                         device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
  814                         sz = x;
  815                 }
  816         } else {
  817                 sz = deflt;
  818         }
  819 
  820         d->bufsz = sz;
  821 
  822         return sz;
  823 }
  824 
  825 int
  826 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
  827 {
  828         struct snddev_info *d = device_get_softc(dev);
  829 
  830         if (pcm_veto_load) {
  831                 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
  832 
  833                 return EINVAL;
  834         }
  835 
  836         d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
  837 
  838 #if 0
  839         /*
  840          * d->flags should be cleared by the allocator of the softc.
  841          * We cannot clear this field here because several devices set
  842          * this flag before calling pcm_register().
  843          */
  844         d->flags = 0;
  845 #endif
  846         d->dev = dev;
  847         d->devinfo = devinfo;
  848         d->devcount = 0;
  849         d->reccount = 0;
  850         d->playcount = 0;
  851         d->vchancount = 0;
  852         d->inprog = 0;
  853 
  854         SLIST_INIT(&d->channels);
  855 
  856         if ((numplay == 0 || numrec == 0) && numplay != numrec)
  857                 d->flags |= SD_F_SIMPLEX;
  858 
  859         d->fakechan = fkchan_setup(dev);
  860         chn_init(d->fakechan, NULL, 0, 0);
  861 
  862 #ifdef SND_DYNSYSCTL
  863         sysctl_ctx_init(&d->sysctl_tree);
  864         d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
  865                                  SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
  866                                  device_get_nameunit(dev), CTLFLAG_RD, 0, "");
  867         if (d->sysctl_tree_top == NULL) {
  868                 sysctl_ctx_free(&d->sysctl_tree);
  869                 goto no;
  870         }
  871         SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
  872             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
  873 #endif
  874         if (numplay > 0) {
  875                 d->flags |= SD_F_AUTOVCHAN;
  876                 vchan_initsys(dev);
  877         }
  878 
  879         sndstat_register(dev, d->status, sndstat_prepare_pcm);
  880         return 0;
  881 no:
  882         snd_mtxfree(d->lock);
  883         return ENXIO;
  884 }
  885 
  886 int
  887 pcm_unregister(device_t dev)
  888 {
  889         struct snddev_info *d = device_get_softc(dev);
  890         struct snddev_channel *sce;
  891         struct pcmchan_children *pce;
  892         struct pcm_channel *ch;
  893 
  894         if (sndstat_acquire() != 0) {
  895                 device_printf(dev, "unregister: sndstat busy\n");
  896                 return EBUSY;
  897         }
  898 
  899         snd_mtxlock(d->lock);
  900         if (d->inprog) {
  901                 device_printf(dev, "unregister: operation in progress\n");
  902                 snd_mtxunlock(d->lock);
  903                 sndstat_release();
  904                 return EBUSY;
  905         }
  906 
  907         SLIST_FOREACH(sce, &d->channels, link) {
  908                 ch = sce->channel;
  909                 if (ch->refcount > 0) {
  910                         device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
  911                         snd_mtxunlock(d->lock);
  912                         sndstat_release();
  913                         return EBUSY;
  914                 }
  915         }
  916 
  917         if (mixer_uninit(dev) == EBUSY) {
  918                 device_printf(dev, "unregister: mixer busy\n");
  919                 snd_mtxunlock(d->lock);
  920                 sndstat_release();
  921                 return EBUSY;
  922         }
  923 
  924         SLIST_FOREACH(sce, &d->channels, link) {
  925                 if (sce->dsp_devt) {
  926                         destroy_dev(sce->dsp_devt);
  927                         sce->dsp_devt = NULL;
  928                 }
  929                 if (sce->dspW_devt) {
  930                         destroy_dev(sce->dspW_devt);
  931                         sce->dspW_devt = NULL;
  932                 }
  933                 if (sce->audio_devt) {
  934                         destroy_dev(sce->audio_devt);
  935                         sce->audio_devt = NULL;
  936                 }
  937                 if (sce->dspr_devt) {
  938                         destroy_dev(sce->dspr_devt);
  939                         sce->dspr_devt = NULL;
  940                 }
  941                 d->devcount--;
  942                 ch = sce->channel;
  943                 if (ch == NULL)
  944                         continue;
  945                 pce = SLIST_FIRST(&ch->children);
  946                 while (pce != NULL) {
  947 #if 0
  948                         device_printf(d->dev, "<%s> removing <%s>\n",
  949                                 ch->name, (pce->channel != NULL) ?
  950                                         pce->channel->name : "unknown");
  951 #endif
  952                         SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
  953                         free(pce, M_DEVBUF);
  954                         pce = SLIST_FIRST(&ch->children);
  955                 }
  956         }
  957 
  958 #ifdef SND_DYNSYSCTL
  959         d->sysctl_tree_top = NULL;
  960         sysctl_ctx_free(&d->sysctl_tree);
  961 #endif
  962 
  963 #if 0
  964         SLIST_FOREACH(sce, &d->channels, link) {
  965                 ch = sce->channel;
  966                 if (ch == NULL)
  967                         continue;
  968                 if (!SLIST_EMPTY(&ch->children))
  969                         device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
  970                                 __func__, ch->name);
  971         }
  972 #endif
  973         while (!SLIST_EMPTY(&d->channels))
  974                 pcm_killchan(dev);
  975 
  976         chn_kill(d->fakechan);
  977         fkchan_kill(d->fakechan);
  978 
  979 #if 0
  980         device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
  981                 "reccount=%u, vchancount=%u\n",
  982                 __func__, d->devcount, d->playcount, d->reccount,
  983                 d->vchancount);
  984 #endif
  985         snd_mtxunlock(d->lock);
  986         snd_mtxfree(d->lock);
  987         sndstat_unregister(dev);
  988         sndstat_release();
  989         return 0;
  990 }
  991 
  992 /************************************************************************/
  993 
  994 static int
  995 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
  996 {
  997         struct snddev_info *d;
  998         struct snddev_channel *sce;
  999         struct pcm_channel *c;
 1000         struct pcm_feeder *f;
 1001         int pc, rc, vc;
 1002 
 1003         if (verbose < 1)
 1004                 return 0;
 1005 
 1006         d = device_get_softc(dev);
 1007         if (!d)
 1008                 return ENXIO;
 1009 
 1010         snd_mtxlock(d->lock);
 1011         if (!SLIST_EMPTY(&d->channels)) {
 1012                 pc = rc = vc = 0;
 1013                 SLIST_FOREACH(sce, &d->channels, link) {
 1014                         c = sce->channel;
 1015                         if (c->direction == PCMDIR_PLAY) {
 1016                                 if (c->flags & CHN_F_VIRTUAL)
 1017                                         vc++;
 1018                                 else
 1019                                         pc++;
 1020                         } else
 1021                                 rc++;
 1022                 }
 1023                 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
 1024                                 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
 1025 #ifdef USING_DEVFS
 1026                                 (device_get_unit(dev) == snd_unit)? " default" : ""
 1027 #else
 1028                                 ""
 1029 #endif
 1030                                 );
 1031 
 1032                 if (verbose <= 1) {
 1033                         snd_mtxunlock(d->lock);
 1034                         return 0;
 1035                 }
 1036 
 1037                 SLIST_FOREACH(sce, &d->channels, link) {
 1038                         c = sce->channel;
 1039 
 1040                         KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
 1041                                 ("hosed pcm channel setup"));
 1042 
 1043                         sbuf_printf(s, "\n\t");
 1044 
 1045                         /* it would be better to indent child channels */
 1046                         sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
 1047                         sbuf_printf(s, "spd %d", c->speed);
 1048                         if (c->speed != sndbuf_getspd(c->bufhard))
 1049                                 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
 1050                         sbuf_printf(s, ", fmt 0x%08x", c->format);
 1051                         if (c->format != sndbuf_getfmt(c->bufhard))
 1052                                 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
 1053                         sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
 1054                         if (c->pid != -1)
 1055                                 sbuf_printf(s, ", pid %d", c->pid);
 1056                         sbuf_printf(s, "\n\t");
 1057 
 1058                         sbuf_printf(s, "interrupts %d, ", c->interrupts);
 1059                         if (c->direction == PCMDIR_REC)
 1060                                 sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
 1061                                         c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
 1062                                         sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
 1063                                         sndbuf_getblkcnt(c->bufhard),
 1064                                         sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
 1065                                         sndbuf_getblkcnt(c->bufsoft));
 1066                         else
 1067                                 sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
 1068                                         c->xruns, sndbuf_getready(c->bufsoft),
 1069                                         sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
 1070                                         sndbuf_getblkcnt(c->bufhard),
 1071                                         sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
 1072                                         sndbuf_getblkcnt(c->bufsoft));
 1073                         sbuf_printf(s, "\n\t");
 1074 
 1075                         sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
 1076                         sbuf_printf(s, " -> ");
 1077                         f = c->feeder;
 1078                         while (f->source != NULL)
 1079                                 f = f->source;
 1080                         while (f != NULL) {
 1081                                 sbuf_printf(s, "%s", f->class->name);
 1082                                 if (f->desc->type == FEEDER_FMT)
 1083                                         sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
 1084                                 if (f->desc->type == FEEDER_RATE)
 1085                                         sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
 1086                                 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
 1087                                                 f->desc->type == FEEDER_VOLUME)
 1088                                         sbuf_printf(s, "(0x%08x)", f->desc->out);
 1089                                 sbuf_printf(s, " -> ");
 1090                                 f = f->parent;
 1091                         }
 1092                         sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
 1093                 }
 1094         } else
 1095                 sbuf_printf(s, " (mixer only)");
 1096         snd_mtxunlock(d->lock);
 1097 
 1098         return 0;
 1099 }
 1100 
 1101 /************************************************************************/
 1102 
 1103 #ifdef SND_DYNSYSCTL
 1104 int
 1105 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
 1106 {
 1107         struct snddev_info *d;
 1108         int err, newcnt;
 1109 
 1110         d = oidp->oid_arg1;
 1111 
 1112         newcnt = d->vchancount;
 1113         err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
 1114 
 1115         if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
 1116                 err = pcm_setvchans(d, newcnt);
 1117 
 1118         return err;
 1119 }
 1120 #endif
 1121 
 1122 /************************************************************************/
 1123 
 1124 static int
 1125 sound_modevent(module_t mod, int type, void *data)
 1126 {
 1127 #if 0
 1128         return (midi_modevent(mod, type, data));
 1129 #else
 1130         return 0;
 1131 #endif
 1132 }
 1133 
 1134 DEV_MODULE(sound, sound_modevent, NULL);
 1135 MODULE_VERSION(sound, SOUND_MODVER);

Cache object: 13f593b93b7b84275129ce0cf9101092


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