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


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