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

Cache object: f7e4d72530eca21f2c4343a58cfaa869


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