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


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

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

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 63b9157d44260a94328b1d58287749c4


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