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) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
    3  * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
    4  * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
    5  * Copyright (c) 1997 Luigi Rizzo
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #ifdef HAVE_KERNEL_OPTION_HEADERS
   31 #include "opt_snd.h"
   32 #endif
   33 
   34 #include <dev/sound/pcm/sound.h>
   35 #include <dev/sound/pcm/ac97.h>
   36 #include <dev/sound/pcm/vchan.h>
   37 #include <dev/sound/pcm/dsp.h>
   38 #include <dev/sound/pcm/sndstat.h>
   39 #include <dev/sound/version.h>
   40 #include <sys/limits.h>
   41 #include <sys/sysctl.h>
   42 
   43 #include "feeder_if.h"
   44 
   45 SND_DECLARE_FILE("$FreeBSD: releng/10.0/sys/dev/sound/pcm/sound.c 243459 2012-11-23 15:31:00Z mav $");
   46 
   47 devclass_t pcm_devclass;
   48 
   49 int pcm_veto_load = 1;
   50 
   51 int snd_unit = -1;
   52 TUNABLE_INT("hw.snd.default_unit", &snd_unit);
   53 
   54 static int snd_unit_auto = -1;
   55 TUNABLE_INT("hw.snd.default_auto", &snd_unit_auto);
   56 SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RW,
   57     &snd_unit_auto, 0, "assign default unit to a newly attached device");
   58 
   59 int snd_maxautovchans = 16;
   60 /* XXX: a tunable implies that we may need more than one sound channel before
   61    the system can change a sysctl (/etc/sysctl.conf), do we really need
   62    this? */
   63 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
   64 
   65 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
   66 
   67 /*
   68  * XXX I've had enough with people not telling proper version/arch
   69  *     while reporting problems, not after 387397913213th questions/requests.
   70  */
   71 static char snd_driver_version[] =
   72     __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
   73 SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
   74     0, "driver version/arch");
   75 
   76 /**
   77  * @brief Unit number allocator for syncgroup IDs
   78  */
   79 struct unrhdr *pcmsg_unrhdr = NULL;
   80 
   81 static int
   82 sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS)
   83 {
   84         SNDSTAT_PREPARE_PCM_BEGIN();
   85         SNDSTAT_PREPARE_PCM_END();
   86 }
   87 
   88 void *
   89 snd_mtxcreate(const char *desc, const char *type)
   90 {
   91         struct mtx *m;
   92 
   93         m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
   94         mtx_init(m, desc, type, MTX_DEF);
   95         return m;
   96 }
   97 
   98 void
   99 snd_mtxfree(void *m)
  100 {
  101         struct mtx *mtx = m;
  102 
  103         mtx_destroy(mtx);
  104         free(mtx, M_DEVBUF);
  105 }
  106 
  107 void
  108 snd_mtxassert(void *m)
  109 {
  110 #ifdef INVARIANTS
  111         struct mtx *mtx = m;
  112 
  113         mtx_assert(mtx, MA_OWNED);
  114 #endif
  115 }
  116 
  117 int
  118 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
  119 {
  120         struct snddev_info *d;
  121 
  122         flags &= INTR_MPSAFE;
  123         flags |= INTR_TYPE_AV;
  124         d = device_get_softc(dev);
  125         if (d != NULL && (flags & INTR_MPSAFE))
  126                 d->flags |= SD_F_MPSAFE;
  127 
  128         return bus_setup_intr(dev, res, flags,
  129 #if __FreeBSD_version >= 700031
  130                         NULL,
  131 #endif
  132                         hand, param, cookiep);
  133 }
  134 
  135 static void
  136 pcm_clonereset(struct snddev_info *d)
  137 {
  138         int cmax;
  139 
  140         PCM_BUSYASSERT(d);
  141 
  142         cmax = d->playcount + d->reccount - 1;
  143         if (d->pvchancount > 0)
  144                 cmax += max(d->pvchancount, snd_maxautovchans) - 1;
  145         if (d->rvchancount > 0)
  146                 cmax += max(d->rvchancount, snd_maxautovchans) - 1;
  147         if (cmax > PCMMAXCLONE)
  148                 cmax = PCMMAXCLONE;
  149         (void)snd_clone_gc(d->clones);
  150         (void)snd_clone_setmaxunit(d->clones, cmax);
  151 }
  152 
  153 int
  154 pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
  155 {
  156         struct pcm_channel *c, *ch, *nch;
  157         struct pcmchan_caps *caps;
  158         int i, err, vcnt;
  159 
  160         PCM_BUSYASSERT(d);
  161 
  162         if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
  163             (direction == PCMDIR_REC && d->reccount < 1))
  164                 return (ENODEV);
  165 
  166         if (!(d->flags & SD_F_AUTOVCHAN))
  167                 return (EINVAL);
  168 
  169         if (newcnt < 0 || newcnt > SND_MAXVCHANS)
  170                 return (E2BIG);
  171 
  172         if (direction == PCMDIR_PLAY)
  173                 vcnt = d->pvchancount;
  174         else if (direction == PCMDIR_REC)
  175                 vcnt = d->rvchancount;
  176         else
  177                 return (EINVAL);
  178 
  179         if (newcnt > vcnt) {
  180                 KASSERT(num == -1 ||
  181                     (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
  182                     ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
  183                     num, newcnt, vcnt));
  184                 /* add new vchans - find a parent channel first */
  185                 ch = NULL;
  186                 CHN_FOREACH(c, d, channels.pcm) {
  187                         CHN_LOCK(c);
  188                         if (c->direction == direction &&
  189                             ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
  190                             c->refcount < 1 &&
  191                             !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
  192                                 /* 
  193                                  * Reuse hw channel with vchans already
  194                                  * created.
  195                                  */
  196                                 if (c->flags & CHN_F_HAS_VCHAN) {
  197                                         ch = c;
  198                                         break;
  199                                 }
  200                                 /*
  201                                  * No vchans ever created, look for
  202                                  * channels with supported formats.
  203                                  */
  204                                 caps = chn_getcaps(c);
  205                                 if (caps == NULL) {
  206                                         CHN_UNLOCK(c);
  207                                         continue;
  208                                 }
  209                                 for (i = 0; caps->fmtlist[i] != 0; i++) {
  210                                         if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
  211                                                 break;
  212                                 }
  213                                 if (caps->fmtlist[i] != 0) {
  214                                         ch = c;
  215                                         break;
  216                                 }
  217                         }
  218                         CHN_UNLOCK(c);
  219                 }
  220                 if (ch == NULL)
  221                         return (EBUSY);
  222                 ch->flags |= CHN_F_BUSY;
  223                 err = 0;
  224                 while (err == 0 && newcnt > vcnt) {
  225                         err = vchan_create(ch, num);
  226                         if (err == 0)
  227                                 vcnt++;
  228                         else if (err == E2BIG && newcnt > vcnt)
  229                                 device_printf(d->dev,
  230                                     "%s: err=%d Maximum channel reached.\n",
  231                                     __func__, err);
  232                 }
  233                 if (vcnt == 0)
  234                         ch->flags &= ~CHN_F_BUSY;
  235                 CHN_UNLOCK(ch);
  236                 if (err != 0)
  237                         return (err);
  238                 else
  239                         pcm_clonereset(d);
  240         } else if (newcnt < vcnt) {
  241                 KASSERT(num == -1,
  242                     ("bogus vchan_destroy() request num=%d", num));
  243                 CHN_FOREACH(c, d, channels.pcm) {
  244                         CHN_LOCK(c);
  245                         if (c->direction != direction ||
  246                             CHN_EMPTY(c, children) ||
  247                             !(c->flags & CHN_F_HAS_VCHAN)) {
  248                                 CHN_UNLOCK(c);
  249                                 continue;
  250                         }
  251                         CHN_FOREACH_SAFE(ch, c, nch, children) {
  252                                 CHN_LOCK(ch);
  253                                 if (vcnt == 1 && c->refcount > 0) {
  254                                         CHN_UNLOCK(ch);
  255                                         break;
  256                                 }
  257                                 if (!(ch->flags & CHN_F_BUSY) &&
  258                                     ch->refcount < 1) {
  259                                         err = vchan_destroy(ch);
  260                                         if (err == 0)
  261                                                 vcnt--;
  262                                 } else
  263                                         CHN_UNLOCK(ch);
  264                                 if (vcnt == newcnt)
  265                                         break;
  266                         }
  267                         CHN_UNLOCK(c);
  268                         break;
  269                 }
  270                 pcm_clonereset(d);
  271         }
  272 
  273         return (0);
  274 }
  275 
  276 /* return error status and a locked channel */
  277 int
  278 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
  279     pid_t pid, char *comm, int devunit)
  280 {
  281         struct pcm_channel *c;
  282         int err, vchancount, vchan_num;
  283 
  284         KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
  285             !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
  286             (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
  287             ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
  288             __func__, d, ch, direction, pid, devunit));
  289         PCM_BUSYASSERT(d);
  290 
  291         /* Double check again. */
  292         if (devunit != -1) {
  293                 switch (snd_unit2d(devunit)) {
  294                 case SND_DEV_DSPHW_PLAY:
  295                 case SND_DEV_DSPHW_VPLAY:
  296                         if (direction != PCMDIR_PLAY)
  297                                 return (ENOTSUP);
  298                         break;
  299                 case SND_DEV_DSPHW_REC:
  300                 case SND_DEV_DSPHW_VREC:
  301                         if (direction != PCMDIR_REC)
  302                                 return (ENOTSUP);
  303                         break;
  304                 default:
  305                         if (!(direction == PCMDIR_PLAY ||
  306                             direction == PCMDIR_REC))
  307                                 return (ENOTSUP);
  308                         break;
  309                 }
  310         }
  311 
  312         *ch = NULL;
  313         vchan_num = 0;
  314         vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
  315             d->rvchancount;
  316 
  317 retry_chnalloc:
  318         err = ENOTSUP;
  319         /* scan for a free channel */
  320         CHN_FOREACH(c, d, channels.pcm) {
  321                 CHN_LOCK(c);
  322                 if (devunit == -1 && c->direction == direction &&
  323                     (c->flags & CHN_F_VIRTUAL)) {
  324                         if (vchancount < snd_maxautovchans &&
  325                             vchan_num < CHN_CHAN(c)) {
  326                                 CHN_UNLOCK(c);
  327                                 goto vchan_alloc;
  328                         }
  329                         vchan_num++;
  330                 }
  331                 if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
  332                     (devunit == -1 || devunit == -2 || c->unit == devunit)) {
  333                         c->flags |= CHN_F_BUSY;
  334                         c->pid = pid;
  335                         strlcpy(c->comm, (comm != NULL) ? comm :
  336                             CHN_COMM_UNKNOWN, sizeof(c->comm));
  337                         *ch = c;
  338                         return (0);
  339                 } else if (c->unit == devunit) {
  340                         if (c->direction != direction)
  341                                 err = ENOTSUP;
  342                         else if (c->flags & CHN_F_BUSY)
  343                                 err = EBUSY;
  344                         else
  345                                 err = EINVAL;
  346                         CHN_UNLOCK(c);
  347                         return (err);
  348                 } else if ((devunit == -1 || devunit == -2) &&
  349                     c->direction == direction && (c->flags & CHN_F_BUSY))
  350                         err = EBUSY;
  351                 CHN_UNLOCK(c);
  352         }
  353 
  354         if (devunit == -2)
  355                 return (err);
  356 
  357 vchan_alloc:
  358         /* no channel available */
  359         if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
  360             snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
  361                 if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
  362                     (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
  363                         return (err);
  364                 err = pcm_setvchans(d, direction, vchancount + 1,
  365                     (devunit == -1) ? -1 : snd_unit2c(devunit));
  366                 if (err == 0) {
  367                         if (devunit == -1)
  368                                 devunit = -2;
  369                         goto retry_chnalloc;
  370                 }
  371         }
  372 
  373         return (err);
  374 }
  375 
  376 /* release a locked channel and unlock it */
  377 int
  378 pcm_chnrelease(struct pcm_channel *c)
  379 {
  380         PCM_BUSYASSERT(c->parentsnddev);
  381         CHN_LOCKASSERT(c);
  382 
  383         c->flags &= ~CHN_F_BUSY;
  384         c->pid = -1;
  385         strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm));
  386         CHN_UNLOCK(c);
  387 
  388         return (0);
  389 }
  390 
  391 int
  392 pcm_chnref(struct pcm_channel *c, int ref)
  393 {
  394         PCM_BUSYASSERT(c->parentsnddev);
  395         CHN_LOCKASSERT(c);
  396 
  397         c->refcount += ref;
  398 
  399         return (c->refcount);
  400 }
  401 
  402 int
  403 pcm_inprog(struct snddev_info *d, int delta)
  404 {
  405         PCM_LOCKASSERT(d);
  406 
  407         d->inprog += delta;
  408 
  409         return (d->inprog);
  410 }
  411 
  412 static void
  413 pcm_setmaxautovchans(struct snddev_info *d, int num)
  414 {
  415         PCM_BUSYASSERT(d);
  416 
  417         if (num < 0)
  418                 return;
  419 
  420         if (num >= 0 && d->pvchancount > num)
  421                 (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
  422         else if (num > 0 && d->pvchancount == 0)
  423                 (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
  424 
  425         if (num >= 0 && d->rvchancount > num)
  426                 (void)pcm_setvchans(d, PCMDIR_REC, num, -1);
  427         else if (num > 0 && d->rvchancount == 0)
  428                 (void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
  429 
  430         pcm_clonereset(d);
  431 }
  432 
  433 static int
  434 sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
  435 {
  436         struct snddev_info *d;
  437         int error, unit;
  438 
  439         unit = snd_unit;
  440         error = sysctl_handle_int(oidp, &unit, 0, req);
  441         if (error == 0 && req->newptr != NULL) {
  442                 d = devclass_get_softc(pcm_devclass, unit);
  443                 if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
  444                         return EINVAL;
  445                 snd_unit = unit;
  446                 snd_unit_auto = 0;
  447         }
  448         return (error);
  449 }
  450 /* XXX: do we need a way to let the user change the default unit? */
  451 SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
  452             0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device");
  453 
  454 static int
  455 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
  456 {
  457         struct snddev_info *d;
  458         int i, v, error;
  459 
  460         v = snd_maxautovchans;
  461         error = sysctl_handle_int(oidp, &v, 0, req);
  462         if (error == 0 && req->newptr != NULL) {
  463                 if (v < 0)
  464                         v = 0;
  465                 if (v > SND_MAXVCHANS)
  466                         v = SND_MAXVCHANS;
  467                 snd_maxautovchans = v;
  468                 for (i = 0; pcm_devclass != NULL &&
  469                     i < devclass_get_maxunit(pcm_devclass); i++) {
  470                         d = devclass_get_softc(pcm_devclass, i);
  471                         if (!PCM_REGISTERED(d))
  472                                 continue;
  473                         PCM_ACQUIRE_QUICK(d);
  474                         pcm_setmaxautovchans(d, v);
  475                         PCM_RELEASE_QUICK(d);
  476                 }
  477         }
  478         return (error);
  479 }
  480 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
  481             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
  482 
  483 struct pcm_channel *
  484 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
  485 {
  486         struct pcm_channel *ch;
  487         int direction, err, rpnum, *pnum, max;
  488         int udc, device, chan;
  489         char *dirs, *devname, buf[CHN_NAMELEN];
  490 
  491         PCM_BUSYASSERT(d);
  492         PCM_LOCKASSERT(d);
  493         KASSERT(num >= -1, ("invalid num=%d", num));
  494 
  495 
  496         switch (dir) {
  497         case PCMDIR_PLAY:
  498                 dirs = "play";
  499                 direction = PCMDIR_PLAY;
  500                 pnum = &d->playcount;
  501                 device = SND_DEV_DSPHW_PLAY;
  502                 max = SND_MAXHWCHAN;
  503                 break;
  504         case PCMDIR_PLAY_VIRTUAL:
  505                 dirs = "virtual";
  506                 direction = PCMDIR_PLAY;
  507                 pnum = &d->pvchancount;
  508                 device = SND_DEV_DSPHW_VPLAY;
  509                 max = SND_MAXVCHANS;
  510                 break;
  511         case PCMDIR_REC:
  512                 dirs = "record";
  513                 direction = PCMDIR_REC;
  514                 pnum = &d->reccount;
  515                 device = SND_DEV_DSPHW_REC;
  516                 max = SND_MAXHWCHAN;
  517                 break;
  518         case PCMDIR_REC_VIRTUAL:
  519                 dirs = "virtual";
  520                 direction = PCMDIR_REC;
  521                 pnum = &d->rvchancount;
  522                 device = SND_DEV_DSPHW_VREC;
  523                 max = SND_MAXVCHANS;
  524                 break;
  525         default:
  526                 return (NULL);
  527         }
  528 
  529         chan = (num == -1) ? 0 : num;
  530 
  531         if (*pnum >= max || chan >= max)
  532                 return (NULL);
  533 
  534         rpnum = 0;
  535 
  536         CHN_FOREACH(ch, d, channels.pcm) {
  537                 if (CHN_DEV(ch) != device)
  538                         continue;
  539                 if (chan == CHN_CHAN(ch)) {
  540                         if (num != -1) {
  541                                 device_printf(d->dev,
  542                                     "channel num=%d allocated!\n", chan);
  543                                 return (NULL);
  544                         }
  545                         chan++;
  546                         if (chan >= max) {
  547                                 device_printf(d->dev,
  548                                     "chan=%d > %d\n", chan, max);
  549                                 return (NULL);
  550                         }
  551                 }
  552                 rpnum++;
  553         }
  554 
  555         if (*pnum != rpnum) {
  556                 device_printf(d->dev,
  557                     "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
  558                     __func__, dirs, *pnum, rpnum);
  559                 return (NULL);
  560         }
  561 
  562         udc = snd_mkunit(device_get_unit(d->dev), device, chan);
  563         devname = dsp_unit2name(buf, sizeof(buf), udc);
  564 
  565         if (devname == NULL) {
  566                 device_printf(d->dev,
  567                     "Failed to query device name udc=0x%08x\n", udc);
  568                 return (NULL);
  569         }
  570 
  571         PCM_UNLOCK(d);
  572         ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
  573         ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
  574         ch->unit = udc;
  575         ch->pid = -1;
  576         strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm));
  577         ch->parentsnddev = d;
  578         ch->parentchannel = parent;
  579         ch->dev = d->dev;
  580         ch->trigger = PCMTRIG_STOP;
  581         snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
  582             device_get_nameunit(ch->dev), dirs, devname);
  583 
  584         err = chn_init(ch, devinfo, dir, direction);
  585         PCM_LOCK(d);
  586         if (err) {
  587                 device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
  588                     ch->name, err);
  589                 kobj_delete(ch->methods, M_DEVBUF);
  590                 free(ch, M_DEVBUF);
  591                 return (NULL);
  592         }
  593 
  594         return (ch);
  595 }
  596 
  597 int
  598 pcm_chn_destroy(struct pcm_channel *ch)
  599 {
  600         struct snddev_info *d;
  601         int err;
  602 
  603         d = ch->parentsnddev;
  604         PCM_BUSYASSERT(d);
  605 
  606         err = chn_kill(ch);
  607         if (err) {
  608                 device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n",
  609                     ch->name, err);
  610                 return (err);
  611         }
  612 
  613         kobj_delete(ch->methods, M_DEVBUF);
  614         free(ch, M_DEVBUF);
  615 
  616         return (0);
  617 }
  618 
  619 int
  620 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
  621 {
  622         PCM_BUSYASSERT(d);
  623         PCM_LOCKASSERT(d);
  624         KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
  625             ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
  626 
  627         CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm);
  628 
  629         switch (CHN_DEV(ch)) {
  630         case SND_DEV_DSPHW_PLAY:
  631                 d->playcount++;
  632                 break;
  633         case SND_DEV_DSPHW_VPLAY:
  634                 d->pvchancount++;
  635                 break;
  636         case SND_DEV_DSPHW_REC:
  637                 d->reccount++;
  638                 break;
  639         case SND_DEV_DSPHW_VREC:
  640                 d->rvchancount++;
  641                 break;
  642         default:
  643                 break;
  644         }
  645 
  646         d->devcount++;
  647 
  648         return (0);
  649 }
  650 
  651 int
  652 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
  653 {
  654         struct pcm_channel *tmp;
  655 
  656         PCM_BUSYASSERT(d);
  657         PCM_LOCKASSERT(d);
  658 
  659         tmp = NULL;
  660 
  661         CHN_FOREACH(tmp, d, channels.pcm) {
  662                 if (tmp == ch)
  663                         break;
  664         }
  665 
  666         if (tmp != ch)
  667                 return (EINVAL);
  668 
  669         CHN_REMOVE(d, ch, channels.pcm);
  670 
  671         switch (CHN_DEV(ch)) {
  672         case SND_DEV_DSPHW_PLAY:
  673                 d->playcount--;
  674                 break;
  675         case SND_DEV_DSPHW_VPLAY:
  676                 d->pvchancount--;
  677                 break;
  678         case SND_DEV_DSPHW_REC:
  679                 d->reccount--;
  680                 break;
  681         case SND_DEV_DSPHW_VREC:
  682                 d->rvchancount--;
  683                 break;
  684         default:
  685                 break;
  686         }
  687 
  688         d->devcount--;
  689 
  690         return (0);
  691 }
  692 
  693 int
  694 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
  695 {
  696         struct snddev_info *d = device_get_softc(dev);
  697         struct pcm_channel *ch;
  698         int err;
  699 
  700         PCM_BUSYASSERT(d);
  701 
  702         PCM_LOCK(d);
  703         ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
  704         if (!ch) {
  705                 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
  706                     cls->name, dir, devinfo);
  707                 PCM_UNLOCK(d);
  708                 return (ENODEV);
  709         }
  710 
  711         err = pcm_chn_add(d, ch);
  712         PCM_UNLOCK(d);
  713         if (err) {
  714                 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
  715                     ch->name, err);
  716                 pcm_chn_destroy(ch);
  717         }
  718 
  719         return (err);
  720 }
  721 
  722 static int
  723 pcm_killchan(device_t dev)
  724 {
  725         struct snddev_info *d = device_get_softc(dev);
  726         struct pcm_channel *ch;
  727         int error;
  728 
  729         PCM_BUSYASSERT(d);
  730 
  731         ch = CHN_FIRST(d, channels.pcm);
  732 
  733         PCM_LOCK(d);
  734         error = pcm_chn_remove(d, ch);
  735         PCM_UNLOCK(d);
  736         if (error)
  737                 return (error);
  738         return (pcm_chn_destroy(ch));
  739 }
  740 
  741 static int
  742 pcm_best_unit(int old)
  743 {
  744         struct snddev_info *d;
  745         int i, best, bestprio, prio;
  746 
  747         best = -1;
  748         bestprio = -100;
  749         for (i = 0; pcm_devclass != NULL &&
  750             i < devclass_get_maxunit(pcm_devclass); i++) {
  751                 d = devclass_get_softc(pcm_devclass, i);
  752                 if (!PCM_REGISTERED(d))
  753                         continue;
  754                 prio = 0;
  755                 if (d->playcount == 0)
  756                         prio -= 10;
  757                 if (d->reccount == 0)
  758                         prio -= 2;
  759                 if (prio > bestprio || (prio == bestprio && i == old)) {
  760                         best = i;
  761                         bestprio = prio;
  762                 }
  763         }
  764         return (best);
  765 }
  766 
  767 int
  768 pcm_setstatus(device_t dev, char *str)
  769 {
  770         struct snddev_info *d = device_get_softc(dev);
  771 
  772         PCM_BUSYASSERT(d);
  773 
  774         if (d->playcount == 0 || d->reccount == 0)
  775                 d->flags |= SD_F_SIMPLEX;
  776 
  777         if ((d->playcount > 0 || d->reccount > 0) &&
  778             !(d->flags & SD_F_AUTOVCHAN)) {
  779                 d->flags |= SD_F_AUTOVCHAN;
  780                 vchan_initsys(dev);
  781         }
  782 
  783         pcm_setmaxautovchans(d, snd_maxautovchans);
  784 
  785         strlcpy(d->status, str, SND_STATUSLEN);
  786 
  787         PCM_LOCK(d);
  788 
  789         /* Last stage, enable cloning. */
  790         if (d->clones != NULL)
  791                 (void)snd_clone_enable(d->clones);
  792 
  793         /* Done, we're ready.. */
  794         d->flags |= SD_F_REGISTERED;
  795 
  796         PCM_RELEASE(d);
  797 
  798         PCM_UNLOCK(d);
  799 
  800         if (snd_unit_auto < 0)
  801                 snd_unit_auto = (snd_unit < 0) ? 1 : 0;
  802         if (snd_unit < 0 || snd_unit_auto > 1)
  803                 snd_unit = device_get_unit(dev);
  804         else if (snd_unit_auto == 1)
  805                 snd_unit = pcm_best_unit(snd_unit);
  806 
  807         return (0);
  808 }
  809 
  810 uint32_t
  811 pcm_getflags(device_t dev)
  812 {
  813         struct snddev_info *d = device_get_softc(dev);
  814 
  815         return d->flags;
  816 }
  817 
  818 void
  819 pcm_setflags(device_t dev, uint32_t val)
  820 {
  821         struct snddev_info *d = device_get_softc(dev);
  822 
  823         d->flags = val;
  824 }
  825 
  826 void *
  827 pcm_getdevinfo(device_t dev)
  828 {
  829         struct snddev_info *d = device_get_softc(dev);
  830 
  831         return d->devinfo;
  832 }
  833 
  834 unsigned int
  835 pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
  836 {
  837         struct snddev_info *d = device_get_softc(dev);
  838         int sz, x;
  839 
  840         sz = 0;
  841         if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
  842                 x = sz;
  843                 RANGE(sz, minbufsz, maxbufsz);
  844                 if (x != sz)
  845                         device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
  846                 x = minbufsz;
  847                 while (x < sz)
  848                         x <<= 1;
  849                 if (x > sz)
  850                         x >>= 1;
  851                 if (x != sz) {
  852                         device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
  853                         sz = x;
  854                 }
  855         } else {
  856                 sz = deflt;
  857         }
  858 
  859         d->bufsz = sz;
  860 
  861         return sz;
  862 }
  863 
  864 static int
  865 sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
  866 {
  867         struct snddev_info *d;
  868         int err, val;
  869 
  870         d = oidp->oid_arg1;
  871         if (!PCM_REGISTERED(d))
  872                 return (ENODEV);
  873 
  874         PCM_LOCK(d);
  875         PCM_WAIT(d);
  876         val = (d->flags & SD_F_BITPERFECT) ? 1 : 0;
  877         PCM_ACQUIRE(d);
  878         PCM_UNLOCK(d);
  879 
  880         err = sysctl_handle_int(oidp, &val, 0, req);
  881 
  882         if (err == 0 && req->newptr != NULL) {
  883                 if (!(val == 0 || val == 1)) {
  884                         PCM_RELEASE_QUICK(d);
  885                         return (EINVAL);
  886                 }
  887 
  888                 PCM_LOCK(d);
  889 
  890                 d->flags &= ~SD_F_BITPERFECT;
  891                 d->flags |= (val != 0) ? SD_F_BITPERFECT : 0;
  892 
  893                 PCM_RELEASE(d);
  894                 PCM_UNLOCK(d);
  895         } else
  896                 PCM_RELEASE_QUICK(d);
  897 
  898         return (err);
  899 }
  900 
  901 #ifdef SND_DEBUG
  902 static int
  903 sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
  904 {
  905         struct snddev_info *d;
  906         uint32_t flags;
  907         int err;
  908 
  909         d = oidp->oid_arg1;
  910         if (!PCM_REGISTERED(d) || d->clones == NULL)
  911                 return (ENODEV);
  912 
  913         PCM_ACQUIRE_QUICK(d);
  914 
  915         flags = snd_clone_getflags(d->clones);
  916         err = sysctl_handle_int(oidp, &flags, 0, req);
  917 
  918         if (err == 0 && req->newptr != NULL) {
  919                 if (flags & ~SND_CLONE_MASK)
  920                         err = EINVAL;
  921                 else
  922                         (void)snd_clone_setflags(d->clones, flags);
  923         }
  924 
  925         PCM_RELEASE_QUICK(d);
  926 
  927         return (err);
  928 }
  929 
  930 static int
  931 sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS)
  932 {
  933         struct snddev_info *d;
  934         int err, deadline;
  935 
  936         d = oidp->oid_arg1;
  937         if (!PCM_REGISTERED(d) || d->clones == NULL)
  938                 return (ENODEV);
  939 
  940         PCM_ACQUIRE_QUICK(d);
  941 
  942         deadline = snd_clone_getdeadline(d->clones);
  943         err = sysctl_handle_int(oidp, &deadline, 0, req);
  944 
  945         if (err == 0 && req->newptr != NULL) {
  946                 if (deadline < 0)
  947                         err = EINVAL;
  948                 else
  949                         (void)snd_clone_setdeadline(d->clones, deadline);
  950         }
  951 
  952         PCM_RELEASE_QUICK(d);
  953 
  954         return (err);
  955 }
  956 
  957 static int
  958 sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS)
  959 {
  960         struct snddev_info *d;
  961         int err, val;
  962 
  963         d = oidp->oid_arg1;
  964         if (!PCM_REGISTERED(d) || d->clones == NULL)
  965                 return (ENODEV);
  966 
  967         val = 0;
  968         err = sysctl_handle_int(oidp, &val, 0, req);
  969 
  970         if (err == 0 && req->newptr != NULL && val != 0) {
  971                 PCM_ACQUIRE_QUICK(d);
  972                 val = snd_clone_gc(d->clones);
  973                 PCM_RELEASE_QUICK(d);
  974                 if (bootverbose != 0 || snd_verbose > 3)
  975                         device_printf(d->dev, "clone gc: pruned=%d\n", val);
  976         }
  977 
  978         return (err);
  979 }
  980 
  981 static int
  982 sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS)
  983 {
  984         struct snddev_info *d;
  985         int i, err, val;
  986 
  987         val = 0;
  988         err = sysctl_handle_int(oidp, &val, 0, req);
  989 
  990         if (err == 0 && req->newptr != NULL && val != 0) {
  991                 for (i = 0; pcm_devclass != NULL &&
  992                     i < devclass_get_maxunit(pcm_devclass); i++) {
  993                         d = devclass_get_softc(pcm_devclass, i);
  994                         if (!PCM_REGISTERED(d) || d->clones == NULL)
  995                                 continue;
  996                         PCM_ACQUIRE_QUICK(d);
  997                         val = snd_clone_gc(d->clones);
  998                         PCM_RELEASE_QUICK(d);
  999                         if (bootverbose != 0 || snd_verbose > 3)
 1000                                 device_printf(d->dev, "clone gc: pruned=%d\n",
 1001                                     val);
 1002                 }
 1003         }
 1004 
 1005         return (err);
 1006 }
 1007 SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW,
 1008     0, sizeof(int), sysctl_hw_snd_clone_gc, "I",
 1009     "global clone garbage collector");
 1010 #endif
 1011 
 1012 int
 1013 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
 1014 {
 1015         struct snddev_info *d;
 1016         int i;
 1017 
 1018         if (pcm_veto_load) {
 1019                 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
 1020 
 1021                 return EINVAL;
 1022         }
 1023 
 1024         if (device_get_unit(dev) > PCMMAXUNIT) {
 1025                 device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n",
 1026                     device_get_unit(dev), PCMMAXUNIT);
 1027                 device_printf(dev,
 1028                     "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
 1029                 return ENODEV;
 1030         }
 1031 
 1032         d = device_get_softc(dev);
 1033         d->dev = dev;
 1034         d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
 1035         cv_init(&d->cv, device_get_nameunit(dev));
 1036         PCM_ACQUIRE_QUICK(d);
 1037         dsp_cdevinfo_init(d);
 1038 #if 0
 1039         /*
 1040          * d->flags should be cleared by the allocator of the softc.
 1041          * We cannot clear this field here because several devices set
 1042          * this flag before calling pcm_register().
 1043          */
 1044         d->flags = 0;
 1045 #endif
 1046         i = 0;
 1047         if (resource_int_value(device_get_name(dev), device_get_unit(dev),
 1048             "vpc", &i) != 0 || i != 0)
 1049                 d->flags |= SD_F_VPC;
 1050 
 1051         if (resource_int_value(device_get_name(dev), device_get_unit(dev),
 1052             "bitperfect", &i) == 0 && i != 0)
 1053                 d->flags |= SD_F_BITPERFECT;
 1054 
 1055         d->devinfo = devinfo;
 1056         d->devcount = 0;
 1057         d->reccount = 0;
 1058         d->playcount = 0;
 1059         d->pvchancount = 0;
 1060         d->rvchancount = 0;
 1061         d->pvchanrate = 0;
 1062         d->pvchanformat = 0;
 1063         d->rvchanrate = 0;
 1064         d->rvchanformat = 0;
 1065         d->inprog = 0;
 1066 
 1067         /*
 1068          * Create clone manager, disabled by default. Cloning will be
 1069          * enabled during final stage of driver initialization through
 1070          * pcm_setstatus().
 1071          */
 1072         d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE,
 1073             SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK |
 1074             SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF |
 1075             SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED);
 1076 
 1077         CHN_INIT(d, channels.pcm);
 1078         CHN_INIT(d, channels.pcm.busy);
 1079         CHN_INIT(d, channels.pcm.opened);
 1080 
 1081         /* XXX This is incorrect, but lets play along for now. */
 1082         if ((numplay == 0 || numrec == 0) && numplay != numrec)
 1083                 d->flags |= SD_F_SIMPLEX;
 1084 
 1085         sysctl_ctx_init(&d->play_sysctl_ctx);
 1086         d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
 1087             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
 1088             CTLFLAG_RD, 0, "playback channels node");
 1089         sysctl_ctx_init(&d->rec_sysctl_ctx);
 1090         d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
 1091             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
 1092             CTLFLAG_RD, 0, "record channels node");
 1093         /* XXX: an user should be able to set this with a control tool, the
 1094            sysadmin then needs min+max sysctls for this */
 1095         SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
 1096             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
 1097             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
 1098         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
 1099             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
 1100             "bitperfect", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
 1101             sysctl_dev_pcm_bitperfect, "I",
 1102             "bit-perfect playback/recording (0=disable, 1=enable)");
 1103 #ifdef SND_DEBUG
 1104         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
 1105             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
 1106             "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d),
 1107             sysctl_dev_pcm_clone_flags, "IU",
 1108             "clone flags");
 1109         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
 1110             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
 1111             "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
 1112             sysctl_dev_pcm_clone_deadline, "I",
 1113             "clone expiration deadline (ms)");
 1114         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
 1115             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
 1116             "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
 1117             sysctl_dev_pcm_clone_gc, "I",
 1118             "clone garbage collector");
 1119 #endif
 1120 
 1121         if (numplay > 0 || numrec > 0) {
 1122                 d->flags |= SD_F_AUTOVCHAN;
 1123                 vchan_initsys(dev);
 1124         }
 1125 
 1126         if (d->flags & SD_F_EQ)
 1127                 feeder_eq_initsys(dev);
 1128 
 1129         sndstat_register(dev, d->status, sndstat_prepare_pcm);
 1130 
 1131         return 0;
 1132 }
 1133 
 1134 int
 1135 pcm_unregister(device_t dev)
 1136 {
 1137         struct snddev_info *d;
 1138         struct pcm_channel *ch;
 1139         struct thread *td;
 1140 
 1141         td = curthread;
 1142         d = device_get_softc(dev);
 1143 
 1144         if (!PCM_ALIVE(d)) {
 1145                 device_printf(dev, "unregister: device not configured\n");
 1146                 return (0);
 1147         }
 1148 
 1149         if (sndstat_acquire(td) != 0) {
 1150                 device_printf(dev, "unregister: sndstat busy\n");
 1151                 return (EBUSY);
 1152         }
 1153 
 1154         PCM_LOCK(d);
 1155         PCM_WAIT(d);
 1156 
 1157         if (d->inprog != 0) {
 1158                 device_printf(dev, "unregister: operation in progress\n");
 1159                 PCM_UNLOCK(d);
 1160                 sndstat_release(td);
 1161                 return (EBUSY);
 1162         }
 1163 
 1164         PCM_ACQUIRE(d);
 1165         PCM_UNLOCK(d);
 1166 
 1167         CHN_FOREACH(ch, d, channels.pcm) {
 1168                 CHN_LOCK(ch);
 1169                 if (ch->refcount > 0) {
 1170                         device_printf(dev,
 1171                             "unregister: channel %s busy (pid %d)\n",
 1172                             ch->name, ch->pid);
 1173                         CHN_UNLOCK(ch);
 1174                         PCM_RELEASE_QUICK(d);
 1175                         sndstat_release(td);
 1176                         return (EBUSY);
 1177                 }
 1178                 CHN_UNLOCK(ch);
 1179         }
 1180 
 1181         if (d->clones != NULL) {
 1182                 if (snd_clone_busy(d->clones) != 0) {
 1183                         device_printf(dev, "unregister: clone busy\n");
 1184                         PCM_RELEASE_QUICK(d);
 1185                         sndstat_release(td);
 1186                         return (EBUSY);
 1187                 } else {
 1188                         PCM_LOCK(d);
 1189                         (void)snd_clone_disable(d->clones);
 1190                         PCM_UNLOCK(d);
 1191                 }
 1192         }
 1193 
 1194         if (mixer_uninit(dev) == EBUSY) {
 1195                 device_printf(dev, "unregister: mixer busy\n");
 1196                 PCM_LOCK(d);
 1197                 if (d->clones != NULL)
 1198                         (void)snd_clone_enable(d->clones);
 1199                 PCM_RELEASE(d);
 1200                 PCM_UNLOCK(d);
 1201                 sndstat_release(td);
 1202                 return (EBUSY);
 1203         }
 1204 
 1205         PCM_LOCK(d);
 1206         d->flags |= SD_F_DYING;
 1207         d->flags &= ~SD_F_REGISTERED;
 1208         PCM_UNLOCK(d);
 1209 
 1210         /*
 1211          * No lock being held, so this thing can be flushed without
 1212          * stucking into devdrn oblivion.
 1213          */
 1214         if (d->clones != NULL) {
 1215                 snd_clone_destroy(d->clones);
 1216                 d->clones = NULL;
 1217         }
 1218 
 1219         if (d->play_sysctl_tree != NULL) {
 1220                 sysctl_ctx_free(&d->play_sysctl_ctx);
 1221                 d->play_sysctl_tree = NULL;
 1222         }
 1223         if (d->rec_sysctl_tree != NULL) {
 1224                 sysctl_ctx_free(&d->rec_sysctl_ctx);
 1225                 d->rec_sysctl_tree = NULL;
 1226         }
 1227 
 1228         while (!CHN_EMPTY(d, channels.pcm))
 1229                 pcm_killchan(dev);
 1230 
 1231         dsp_cdevinfo_flush(d);
 1232 
 1233         PCM_LOCK(d);
 1234         PCM_RELEASE(d);
 1235         cv_destroy(&d->cv);
 1236         PCM_UNLOCK(d);
 1237         snd_mtxfree(d->lock);
 1238         sndstat_unregister(dev);
 1239         sndstat_release(td);
 1240 
 1241         if (snd_unit == device_get_unit(dev)) {
 1242                 snd_unit = pcm_best_unit(-1);
 1243                 if (snd_unit_auto == 0)
 1244                         snd_unit_auto = 1;
 1245         }
 1246 
 1247         return (0);
 1248 }
 1249 
 1250 /************************************************************************/
 1251 
 1252 /**
 1253  * @brief       Handle OSSv4 SNDCTL_SYSINFO ioctl.
 1254  *
 1255  * @param si    Pointer to oss_sysinfo struct where information about the
 1256  *              sound subsystem will be written/copied.
 1257  *
 1258  * This routine returns information about the sound system, such as the
 1259  * current OSS version, number of audio, MIDI, and mixer drivers, etc.
 1260  * Also includes a bitmask showing which of the above types of devices
 1261  * are open (busy).
 1262  *
 1263  * @note
 1264  * Calling threads must not hold any snddev_info or pcm_channel locks.
 1265  *
 1266  * @author      Ryan Beasley <ryanb@FreeBSD.org>
 1267  */
 1268 void
 1269 sound_oss_sysinfo(oss_sysinfo *si)
 1270 {
 1271         static char si_product[] = "FreeBSD native OSS ABI";
 1272         static char si_version[] = __XSTRING(__FreeBSD_version);
 1273         static char si_license[] = "BSD";
 1274         static int intnbits = sizeof(int) * 8;  /* Better suited as macro?
 1275                                                    Must pester a C guru. */
 1276 
 1277         struct snddev_info *d;
 1278         struct pcm_channel *c;
 1279         int i, j, ncards;
 1280         
 1281         ncards = 0;
 1282 
 1283         strlcpy(si->product, si_product, sizeof(si->product));
 1284         strlcpy(si->version, si_version, sizeof(si->version));
 1285         si->versionnum = SOUND_VERSION;
 1286         strlcpy(si->license, si_license, sizeof(si->license));
 1287 
 1288         /*
 1289          * Iterate over PCM devices and their channels, gathering up data
 1290          * for the numaudios, ncards, and openedaudio fields.
 1291          */
 1292         si->numaudios = 0;
 1293         bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
 1294 
 1295         j = 0;
 1296 
 1297         for (i = 0; pcm_devclass != NULL &&
 1298             i < devclass_get_maxunit(pcm_devclass); i++) {
 1299                 d = devclass_get_softc(pcm_devclass, i);
 1300                 if (!PCM_REGISTERED(d))
 1301                         continue;
 1302 
 1303                 /* XXX Need Giant magic entry ??? */
 1304 
 1305                 /* See note in function's docblock */
 1306                 PCM_UNLOCKASSERT(d);
 1307                 PCM_LOCK(d);
 1308 
 1309                 si->numaudios += d->devcount;
 1310                 ++ncards;
 1311 
 1312                 CHN_FOREACH(c, d, channels.pcm) {
 1313                         CHN_UNLOCKASSERT(c);
 1314                         CHN_LOCK(c);
 1315                         if (c->flags & CHN_F_BUSY)
 1316                                 si->openedaudio[j / intnbits] |=
 1317                                     (1 << (j % intnbits));
 1318                         CHN_UNLOCK(c);
 1319                         j++;
 1320                 }
 1321 
 1322                 PCM_UNLOCK(d);
 1323         }
 1324         si->numaudioengines = si->numaudios;
 1325 
 1326         si->numsynths = 0;      /* OSSv4 docs:  this field is obsolete */
 1327         /**
 1328          * @todo        Collect num{midis,timers}.
 1329          *
 1330          * Need access to sound/midi/midi.c::midistat_lock in order
 1331          * to safely touch midi_devices and get a head count of, well,
 1332          * MIDI devices.  midistat_lock is a global static (i.e., local to
 1333          * midi.c), but midi_devices is a regular global; should the mutex
 1334          * be publicized, or is there another way to get this information?
 1335          *
 1336          * NB:  MIDI/sequencer stuff is currently on hold.
 1337          */
 1338         si->nummidis = 0;
 1339         si->numtimers = 0;
 1340         si->nummixers = mixer_count;
 1341         si->numcards = ncards;
 1342                 /* OSSv4 docs:  Intended only for test apps; API doesn't
 1343                    really have much of a concept of cards.  Shouldn't be
 1344                    used by applications. */
 1345 
 1346         /**
 1347          * @todo        Fill in "busy devices" fields.
 1348          *
 1349          *  si->openedmidi = " MIDI devices
 1350          */
 1351         bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
 1352 
 1353         /*
 1354          * Si->filler is a reserved array, but according to docs each
 1355          * element should be set to -1.
 1356          */
 1357         for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
 1358                 si->filler[i] = -1;
 1359 }
 1360 
 1361 int
 1362 sound_oss_card_info(oss_card_info *si)
 1363 {
 1364         struct snddev_info *d;
 1365         int i, ncards;
 1366         
 1367         ncards = 0;
 1368 
 1369         for (i = 0; pcm_devclass != NULL &&
 1370             i < devclass_get_maxunit(pcm_devclass); i++) {
 1371                 d = devclass_get_softc(pcm_devclass, i);
 1372                 if (!PCM_REGISTERED(d))
 1373                         continue;
 1374 
 1375                 if (ncards++ != si->card)
 1376                         continue;
 1377 
 1378                 PCM_UNLOCKASSERT(d);
 1379                 PCM_LOCK(d);
 1380                 
 1381                 strlcpy(si->shortname, device_get_nameunit(d->dev),
 1382                     sizeof(si->shortname));
 1383                 strlcpy(si->longname, device_get_desc(d->dev),
 1384                     sizeof(si->longname));
 1385                 strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
 1386                 si->intr_count = si->ack_count = 0;
 1387 
 1388                 PCM_UNLOCK(d);
 1389 
 1390                 return (0);
 1391         }
 1392         return (ENXIO);
 1393 }
 1394 
 1395 /************************************************************************/
 1396 
 1397 static int
 1398 sound_modevent(module_t mod, int type, void *data)
 1399 {
 1400         int ret;
 1401 #if 0
 1402         return (midi_modevent(mod, type, data));
 1403 #else
 1404         ret = 0;
 1405 
 1406         switch(type) {
 1407                 case MOD_LOAD:
 1408                         pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
 1409                         break;
 1410                 case MOD_UNLOAD:
 1411                         ret = sndstat_acquire(curthread);
 1412                         if (ret != 0)
 1413                                 break;
 1414                         if (pcmsg_unrhdr != NULL) {
 1415                                 delete_unrhdr(pcmsg_unrhdr);
 1416                                 pcmsg_unrhdr = NULL;
 1417                         }
 1418                         break;
 1419                 case MOD_SHUTDOWN:
 1420                         break;
 1421                 default:
 1422                         ret = ENOTSUP;
 1423         }
 1424 
 1425         return ret;
 1426 #endif
 1427 }
 1428 
 1429 DEV_MODULE(sound, sound_modevent, NULL);
 1430 MODULE_VERSION(sound, SOUND_MODVER);

Cache object: f9585d4a2e518e8697f24ae1aa356595


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