The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/sound/pcm/sound.c

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

    1 /*-
    2  * Copyright (c) 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/8.4/sys/dev/sound/pcm/sound.c 236745 2012-06-08 11:58:28Z 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         if (bootverbose != 0 || snd_verbose > 3) {
 1078                 device_printf(dev,
 1079                     "clone manager: deadline=%dms flags=0x%08x\n",
 1080                     snd_clone_getdeadline(d->clones),
 1081                     snd_clone_getflags(d->clones));
 1082         }
 1083 
 1084         CHN_INIT(d, channels.pcm);
 1085         CHN_INIT(d, channels.pcm.busy);
 1086         CHN_INIT(d, channels.pcm.opened);
 1087 
 1088         /* XXX This is incorrect, but lets play along for now. */
 1089         if ((numplay == 0 || numrec == 0) && numplay != numrec)
 1090                 d->flags |= SD_F_SIMPLEX;
 1091 
 1092         sysctl_ctx_init(&d->play_sysctl_ctx);
 1093         d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
 1094             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
 1095             CTLFLAG_RD, 0, "playback channels node");
 1096         sysctl_ctx_init(&d->rec_sysctl_ctx);
 1097         d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
 1098             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
 1099             CTLFLAG_RD, 0, "record channels node");
 1100         /* XXX: an user should be able to set this with a control tool, the
 1101            sysadmin then needs min+max sysctls for this */
 1102         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
 1103             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
 1104             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
 1105         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
 1106             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
 1107             "bitperfect", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
 1108             sysctl_dev_pcm_bitperfect, "I",
 1109             "bit-perfect playback/recording (0=disable, 1=enable)");
 1110 #ifdef SND_DEBUG
 1111         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
 1112             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
 1113             "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d),
 1114             sysctl_dev_pcm_clone_flags, "IU",
 1115             "clone flags");
 1116         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
 1117             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
 1118             "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
 1119             sysctl_dev_pcm_clone_deadline, "I",
 1120             "clone expiration deadline (ms)");
 1121         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
 1122             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
 1123             "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
 1124             sysctl_dev_pcm_clone_gc, "I",
 1125             "clone garbage collector");
 1126 #endif
 1127 
 1128         if (numplay > 0 || numrec > 0) {
 1129                 d->flags |= SD_F_AUTOVCHAN;
 1130                 vchan_initsys(dev);
 1131         }
 1132 
 1133         if (d->flags & SD_F_EQ)
 1134                 feeder_eq_initsys(dev);
 1135 
 1136         sndstat_register(dev, d->status, sndstat_prepare_pcm);
 1137 
 1138         return 0;
 1139 }
 1140 
 1141 int
 1142 pcm_unregister(device_t dev)
 1143 {
 1144         struct snddev_info *d;
 1145         struct pcm_channel *ch;
 1146         struct thread *td;
 1147 
 1148         td = curthread;
 1149         d = device_get_softc(dev);
 1150 
 1151         if (!PCM_ALIVE(d)) {
 1152                 device_printf(dev, "unregister: device not configured\n");
 1153                 return (0);
 1154         }
 1155 
 1156         if (sndstat_acquire(td) != 0) {
 1157                 device_printf(dev, "unregister: sndstat busy\n");
 1158                 return (EBUSY);
 1159         }
 1160 
 1161         PCM_LOCK(d);
 1162         PCM_WAIT(d);
 1163 
 1164         if (d->inprog != 0) {
 1165                 device_printf(dev, "unregister: operation in progress\n");
 1166                 PCM_UNLOCK(d);
 1167                 sndstat_release(td);
 1168                 return (EBUSY);
 1169         }
 1170 
 1171         PCM_ACQUIRE(d);
 1172         PCM_UNLOCK(d);
 1173 
 1174         CHN_FOREACH(ch, d, channels.pcm) {
 1175                 CHN_LOCK(ch);
 1176                 if (ch->refcount > 0) {
 1177                         device_printf(dev,
 1178                             "unregister: channel %s busy (pid %d)\n",
 1179                             ch->name, ch->pid);
 1180                         CHN_UNLOCK(ch);
 1181                         PCM_RELEASE_QUICK(d);
 1182                         sndstat_release(td);
 1183                         return (EBUSY);
 1184                 }
 1185                 CHN_UNLOCK(ch);
 1186         }
 1187 
 1188         if (d->clones != NULL) {
 1189                 if (snd_clone_busy(d->clones) != 0) {
 1190                         device_printf(dev, "unregister: clone busy\n");
 1191                         PCM_RELEASE_QUICK(d);
 1192                         sndstat_release(td);
 1193                         return (EBUSY);
 1194                 } else {
 1195                         PCM_LOCK(d);
 1196                         (void)snd_clone_disable(d->clones);
 1197                         PCM_UNLOCK(d);
 1198                 }
 1199         }
 1200 
 1201         if (mixer_uninit(dev) == EBUSY) {
 1202                 device_printf(dev, "unregister: mixer busy\n");
 1203                 PCM_LOCK(d);
 1204                 if (d->clones != NULL)
 1205                         (void)snd_clone_enable(d->clones);
 1206                 PCM_RELEASE(d);
 1207                 PCM_UNLOCK(d);
 1208                 sndstat_release(td);
 1209                 return (EBUSY);
 1210         }
 1211 
 1212         PCM_LOCK(d);
 1213         d->flags |= SD_F_DYING;
 1214         d->flags &= ~SD_F_REGISTERED;
 1215         PCM_UNLOCK(d);
 1216 
 1217         /*
 1218          * No lock being held, so this thing can be flushed without
 1219          * stucking into devdrn oblivion.
 1220          */
 1221         if (d->clones != NULL) {
 1222                 snd_clone_destroy(d->clones);
 1223                 d->clones = NULL;
 1224         }
 1225 
 1226         if (d->play_sysctl_tree != NULL) {
 1227                 sysctl_ctx_free(&d->play_sysctl_ctx);
 1228                 d->play_sysctl_tree = NULL;
 1229         }
 1230         if (d->rec_sysctl_tree != NULL) {
 1231                 sysctl_ctx_free(&d->rec_sysctl_ctx);
 1232                 d->rec_sysctl_tree = NULL;
 1233         }
 1234 
 1235         while (!CHN_EMPTY(d, channels.pcm))
 1236                 pcm_killchan(dev);
 1237 
 1238         dsp_cdevinfo_flush(d);
 1239 
 1240         PCM_LOCK(d);
 1241         PCM_RELEASE(d);
 1242         cv_destroy(&d->cv);
 1243         PCM_UNLOCK(d);
 1244         snd_mtxfree(d->lock);
 1245         sndstat_unregister(dev);
 1246         sndstat_release(td);
 1247 
 1248         if (snd_unit == device_get_unit(dev)) {
 1249                 snd_unit = pcm_best_unit(-1);
 1250                 if (snd_unit_auto == 0)
 1251                         snd_unit_auto = 1;
 1252         }
 1253 
 1254         return (0);
 1255 }
 1256 
 1257 /************************************************************************/
 1258 
 1259 /**
 1260  * @brief       Handle OSSv4 SNDCTL_SYSINFO ioctl.
 1261  *
 1262  * @param si    Pointer to oss_sysinfo struct where information about the
 1263  *              sound subsystem will be written/copied.
 1264  *
 1265  * This routine returns information about the sound system, such as the
 1266  * current OSS version, number of audio, MIDI, and mixer drivers, etc.
 1267  * Also includes a bitmask showing which of the above types of devices
 1268  * are open (busy).
 1269  *
 1270  * @note
 1271  * Calling threads must not hold any snddev_info or pcm_channel locks.
 1272  *
 1273  * @author      Ryan Beasley <ryanb@FreeBSD.org>
 1274  */
 1275 void
 1276 sound_oss_sysinfo(oss_sysinfo *si)
 1277 {
 1278         static char si_product[] = "FreeBSD native OSS ABI";
 1279         static char si_version[] = __XSTRING(__FreeBSD_version);
 1280         static char si_license[] = "BSD";
 1281         static int intnbits = sizeof(int) * 8;  /* Better suited as macro?
 1282                                                    Must pester a C guru. */
 1283 
 1284         struct snddev_info *d;
 1285         struct pcm_channel *c;
 1286         int i, j, ncards;
 1287         
 1288         ncards = 0;
 1289 
 1290         strlcpy(si->product, si_product, sizeof(si->product));
 1291         strlcpy(si->version, si_version, sizeof(si->version));
 1292         si->versionnum = SOUND_VERSION;
 1293         strlcpy(si->license, si_license, sizeof(si->license));
 1294 
 1295         /*
 1296          * Iterate over PCM devices and their channels, gathering up data
 1297          * for the numaudios, ncards, and openedaudio fields.
 1298          */
 1299         si->numaudios = 0;
 1300         bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
 1301 
 1302         j = 0;
 1303 
 1304         for (i = 0; pcm_devclass != NULL &&
 1305             i < devclass_get_maxunit(pcm_devclass); i++) {
 1306                 d = devclass_get_softc(pcm_devclass, i);
 1307                 if (!PCM_REGISTERED(d))
 1308                         continue;
 1309 
 1310                 /* XXX Need Giant magic entry ??? */
 1311 
 1312                 /* See note in function's docblock */
 1313                 PCM_UNLOCKASSERT(d);
 1314                 PCM_LOCK(d);
 1315 
 1316                 si->numaudios += d->devcount;
 1317                 ++ncards;
 1318 
 1319                 CHN_FOREACH(c, d, channels.pcm) {
 1320                         CHN_UNLOCKASSERT(c);
 1321                         CHN_LOCK(c);
 1322                         if (c->flags & CHN_F_BUSY)
 1323                                 si->openedaudio[j / intnbits] |=
 1324                                     (1 << (j % intnbits));
 1325                         CHN_UNLOCK(c);
 1326                         j++;
 1327                 }
 1328 
 1329                 PCM_UNLOCK(d);
 1330         }
 1331         si->numaudioengines = si->numaudios;
 1332 
 1333         si->numsynths = 0;      /* OSSv4 docs:  this field is obsolete */
 1334         /**
 1335          * @todo        Collect num{midis,timers}.
 1336          *
 1337          * Need access to sound/midi/midi.c::midistat_lock in order
 1338          * to safely touch midi_devices and get a head count of, well,
 1339          * MIDI devices.  midistat_lock is a global static (i.e., local to
 1340          * midi.c), but midi_devices is a regular global; should the mutex
 1341          * be publicized, or is there another way to get this information?
 1342          *
 1343          * NB:  MIDI/sequencer stuff is currently on hold.
 1344          */
 1345         si->nummidis = 0;
 1346         si->numtimers = 0;
 1347         si->nummixers = mixer_count;
 1348         si->numcards = ncards;
 1349                 /* OSSv4 docs:  Intended only for test apps; API doesn't
 1350                    really have much of a concept of cards.  Shouldn't be
 1351                    used by applications. */
 1352 
 1353         /**
 1354          * @todo        Fill in "busy devices" fields.
 1355          *
 1356          *  si->openedmidi = " MIDI devices
 1357          */
 1358         bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
 1359 
 1360         /*
 1361          * Si->filler is a reserved array, but according to docs each
 1362          * element should be set to -1.
 1363          */
 1364         for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
 1365                 si->filler[i] = -1;
 1366 }
 1367 
 1368 int
 1369 sound_oss_card_info(oss_card_info *si)
 1370 {
 1371         struct snddev_info *d;
 1372         int i, ncards;
 1373         
 1374         ncards = 0;
 1375 
 1376         for (i = 0; pcm_devclass != NULL &&
 1377             i < devclass_get_maxunit(pcm_devclass); i++) {
 1378                 d = devclass_get_softc(pcm_devclass, i);
 1379                 if (!PCM_REGISTERED(d))
 1380                         continue;
 1381 
 1382                 if (ncards++ != si->card)
 1383                         continue;
 1384 
 1385                 PCM_UNLOCKASSERT(d);
 1386                 PCM_LOCK(d);
 1387                 
 1388                 strlcpy(si->shortname, device_get_nameunit(d->dev),
 1389                     sizeof(si->shortname));
 1390                 strlcpy(si->longname, device_get_desc(d->dev),
 1391                     sizeof(si->longname));
 1392                 strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
 1393                 si->intr_count = si->ack_count = 0;
 1394 
 1395                 PCM_UNLOCK(d);
 1396 
 1397                 return (0);
 1398         }
 1399         return (ENXIO);
 1400 }
 1401 
 1402 /************************************************************************/
 1403 
 1404 static int
 1405 sound_modevent(module_t mod, int type, void *data)
 1406 {
 1407         int ret;
 1408 #if 0
 1409         return (midi_modevent(mod, type, data));
 1410 #else
 1411         ret = 0;
 1412 
 1413         switch(type) {
 1414                 case MOD_LOAD:
 1415                         pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
 1416                         break;
 1417                 case MOD_UNLOAD:
 1418                         ret = sndstat_acquire(curthread);
 1419                         if (ret != 0)
 1420                                 break;
 1421                         if (pcmsg_unrhdr != NULL) {
 1422                                 delete_unrhdr(pcmsg_unrhdr);
 1423                                 pcmsg_unrhdr = NULL;
 1424                         }
 1425                         break;
 1426                 case MOD_SHUTDOWN:
 1427                         break;
 1428                 default:
 1429                         ret = ENOTSUP;
 1430         }
 1431 
 1432         return ret;
 1433 #endif
 1434 }
 1435 
 1436 DEV_MODULE(sound, sound_modevent, NULL);
 1437 MODULE_VERSION(sound, SOUND_MODVER);

Cache object: 370c6806f64b8112489dd4183919852e


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