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/sndstat.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  * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
    6  * Copyright (c) 2020 The FreeBSD Foundation
    7  * All rights reserved.
    8  *
    9  * Portions of this software were developed by Ka Ho Ng
   10  * under sponsorship from the FreeBSD Foundation.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   31  * SUCH DAMAGE.
   32  */
   33 
   34 #ifdef HAVE_KERNEL_OPTION_HEADERS
   35 #include "opt_snd.h"
   36 #endif
   37 
   38 #include <sys/param.h>
   39 #include <sys/lock.h>
   40 #include <sys/malloc.h>
   41 #include <sys/nv.h>
   42 #include <sys/dnv.h>
   43 #include <sys/sx.h>
   44 #ifdef COMPAT_FREEBSD32
   45 #include <sys/sysent.h>
   46 #endif
   47 
   48 #include <dev/sound/pcm/sound.h>
   49 #include <dev/sound/pcm/pcm.h>
   50 #include <dev/sound/version.h>
   51 
   52 
   53 SND_DECLARE_FILE("$FreeBSD$");
   54 
   55 #define SS_TYPE_MODULE          0
   56 #define SS_TYPE_PCM             1
   57 #define SS_TYPE_MIDI            2
   58 #define SS_TYPE_SEQUENCER       3
   59 
   60 static d_open_t sndstat_open;
   61 static void sndstat_close(void *);
   62 static d_read_t sndstat_read;
   63 static d_write_t sndstat_write;
   64 static d_ioctl_t sndstat_ioctl;
   65 
   66 static struct cdevsw sndstat_cdevsw = {
   67         .d_version =    D_VERSION,
   68         .d_open =       sndstat_open,
   69         .d_read =       sndstat_read,
   70         .d_write =      sndstat_write,
   71         .d_ioctl =      sndstat_ioctl,
   72         .d_name =       "sndstat",
   73         .d_flags =      D_TRACKCLOSE,
   74 };
   75 
   76 struct sndstat_entry {
   77         TAILQ_ENTRY(sndstat_entry) link;
   78         device_t dev;
   79         char *str;
   80         sndstat_handler handler;
   81         int type, unit;
   82 };
   83 
   84 struct sndstat_userdev {
   85         TAILQ_ENTRY(sndstat_userdev) link;
   86         char *provider;
   87         char *nameunit;
   88         char *devnode;
   89         char *desc;
   90         unsigned int pchan;
   91         unsigned int rchan;
   92         struct {
   93                 uint32_t min_rate;
   94                 uint32_t max_rate;
   95                 uint32_t formats;
   96                 uint32_t min_chn;
   97                 uint32_t max_chn;
   98         } info_play, info_rec;
   99         nvlist_t *provider_nvl;
  100 };
  101 
  102 struct sndstat_file {
  103         TAILQ_ENTRY(sndstat_file) entry;
  104         struct sbuf sbuf;
  105         struct sx lock;
  106         void *devs_nvlbuf;      /* (l) */
  107         size_t devs_nbytes;     /* (l) */
  108         TAILQ_HEAD(, sndstat_userdev) userdev_list;     /* (l) */
  109         int out_offset;
  110         int in_offset;
  111         int fflags;
  112 };
  113 
  114 static struct sx sndstat_lock;
  115 static struct cdev *sndstat_dev;
  116 
  117 #define SNDSTAT_LOCK() sx_xlock(&sndstat_lock)
  118 #define SNDSTAT_UNLOCK() sx_xunlock(&sndstat_lock)
  119 
  120 static TAILQ_HEAD(, sndstat_entry) sndstat_devlist = TAILQ_HEAD_INITIALIZER(sndstat_devlist);
  121 static TAILQ_HEAD(, sndstat_file) sndstat_filelist = TAILQ_HEAD_INITIALIZER(sndstat_filelist);
  122 
  123 int snd_verbose = 0;
  124 
  125 static int sndstat_prepare(struct sndstat_file *);
  126 static struct sndstat_userdev *
  127 sndstat_line2userdev(struct sndstat_file *, const char *, int);
  128 
  129 static int
  130 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
  131 {
  132         int error, verbose;
  133 
  134         verbose = snd_verbose;
  135         error = sysctl_handle_int(oidp, &verbose, 0, req);
  136         if (error == 0 && req->newptr != NULL) {
  137                 if (verbose < 0 || verbose > 4)
  138                         error = EINVAL;
  139                 else
  140                         snd_verbose = verbose;
  141         }
  142         return (error);
  143 }
  144 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose,
  145     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
  146     sysctl_hw_sndverbose, "I",
  147     "verbosity level");
  148 
  149 static int
  150 sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
  151 {
  152         struct sndstat_file *pf;
  153 
  154         pf = malloc(sizeof(*pf), M_DEVBUF, M_WAITOK | M_ZERO);
  155 
  156         if (sbuf_new(&pf->sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
  157                 free(pf, M_DEVBUF);
  158                 return (ENOMEM);
  159         }
  160 
  161         pf->fflags = flags;
  162         TAILQ_INIT(&pf->userdev_list);
  163         sx_init(&pf->lock, "sndstat_file");
  164 
  165         SNDSTAT_LOCK();
  166         TAILQ_INSERT_TAIL(&sndstat_filelist, pf, entry);
  167         SNDSTAT_UNLOCK();
  168 
  169         devfs_set_cdevpriv(pf, &sndstat_close);
  170 
  171         return (0);
  172 }
  173 
  174 /*
  175  * Should only be called either when:
  176  * * Closing
  177  * * pf->lock held
  178  */
  179 static void
  180 sndstat_remove_all_userdevs(struct sndstat_file *pf)
  181 {
  182         struct sndstat_userdev *ud;
  183 
  184         KASSERT(
  185             sx_xlocked(&pf->lock), ("%s: Called without pf->lock", __func__));
  186         while ((ud = TAILQ_FIRST(&pf->userdev_list)) != NULL) {
  187                 TAILQ_REMOVE(&pf->userdev_list, ud, link);
  188                 free(ud->provider, M_DEVBUF);
  189                 free(ud->desc, M_DEVBUF);
  190                 free(ud->devnode, M_DEVBUF);
  191                 free(ud->nameunit, M_DEVBUF);
  192                 nvlist_destroy(ud->provider_nvl);
  193                 free(ud, M_DEVBUF);
  194         }
  195 }
  196 
  197 static void
  198 sndstat_close(void *sndstat_file)
  199 {
  200         struct sndstat_file *pf = (struct sndstat_file *)sndstat_file;
  201 
  202         SNDSTAT_LOCK();
  203         sbuf_delete(&pf->sbuf);
  204         TAILQ_REMOVE(&sndstat_filelist, pf, entry);
  205         SNDSTAT_UNLOCK();
  206 
  207         free(pf->devs_nvlbuf, M_NVLIST);
  208         sx_xlock(&pf->lock);
  209         sndstat_remove_all_userdevs(pf);
  210         sx_xunlock(&pf->lock);
  211         sx_destroy(&pf->lock);
  212 
  213         free(pf, M_DEVBUF);
  214 }
  215 
  216 static int
  217 sndstat_read(struct cdev *i_dev, struct uio *buf, int flag)
  218 {
  219         struct sndstat_file *pf;
  220         int err;
  221         int len;
  222 
  223         err = devfs_get_cdevpriv((void **)&pf);
  224         if (err != 0)
  225                 return (err);
  226 
  227         /* skip zero-length reads */
  228         if (buf->uio_resid == 0)
  229                 return (0);
  230 
  231         SNDSTAT_LOCK();
  232         if (pf->out_offset != 0) {
  233                 /* don't allow both reading and writing */
  234                 err = EINVAL;
  235                 goto done;
  236         } else if (pf->in_offset == 0) {
  237                 err = sndstat_prepare(pf);
  238                 if (err <= 0) {
  239                         err = ENOMEM;
  240                         goto done;
  241                 }
  242         }
  243         len = sbuf_len(&pf->sbuf) - pf->in_offset;
  244         if (len > buf->uio_resid)
  245                 len = buf->uio_resid;
  246         if (len > 0)
  247                 err = uiomove(sbuf_data(&pf->sbuf) + pf->in_offset, len, buf);
  248         pf->in_offset += len;
  249 done:
  250         SNDSTAT_UNLOCK();
  251         return (err);
  252 }
  253 
  254 static int
  255 sndstat_write(struct cdev *i_dev, struct uio *buf, int flag)
  256 {
  257         struct sndstat_file *pf;
  258         uint8_t temp[64];
  259         int err;
  260         int len;
  261 
  262         err = devfs_get_cdevpriv((void **)&pf);
  263         if (err != 0)
  264                 return (err);
  265 
  266         /* skip zero-length writes */
  267         if (buf->uio_resid == 0)
  268                 return (0);
  269 
  270         /* don't allow writing more than 64Kbytes */
  271         if (buf->uio_resid > 65536)
  272                 return (ENOMEM);
  273 
  274         SNDSTAT_LOCK();
  275         if (pf->in_offset != 0) {
  276                 /* don't allow both reading and writing */
  277                 err = EINVAL;
  278         } else {
  279                 /* only remember the last write - allows for updates */
  280                 sx_xlock(&pf->lock);
  281                 sndstat_remove_all_userdevs(pf);
  282                 sx_xunlock(&pf->lock);
  283 
  284                 while (1) {
  285                         len = sizeof(temp);
  286                         if (len > buf->uio_resid)
  287                                 len = buf->uio_resid;
  288                         if (len > 0) {
  289                                 err = uiomove(temp, len, buf);
  290                                 if (err)
  291                                         break;
  292                         } else {
  293                                 break;
  294                         }
  295                         if (sbuf_bcat(&pf->sbuf, temp, len) < 0) {
  296                                 err = ENOMEM;
  297                                 break;
  298                         }
  299                 }
  300                 sbuf_finish(&pf->sbuf);
  301 
  302                 if (err == 0) {
  303                         char *line, *str;
  304 
  305                         str = sbuf_data(&pf->sbuf);
  306                         while ((line = strsep(&str, "\n")) != NULL) {
  307                                 struct sndstat_userdev *ud;
  308 
  309                                 ud = sndstat_line2userdev(pf, line, strlen(line));
  310                                 if (ud == NULL)
  311                                         continue;
  312 
  313                                 sx_xlock(&pf->lock);
  314                                 TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link);
  315                                 sx_xunlock(&pf->lock);
  316                         }
  317 
  318                         pf->out_offset = sbuf_len(&pf->sbuf);
  319                 } else
  320                         pf->out_offset = 0;
  321 
  322                 sbuf_clear(&pf->sbuf);
  323         }
  324         SNDSTAT_UNLOCK();
  325         return (err);
  326 }
  327 
  328 static void
  329 sndstat_get_caps(struct snddev_info *d, bool play, uint32_t *min_rate,
  330     uint32_t *max_rate, uint32_t *fmts, uint32_t *minchn, uint32_t *maxchn)
  331 {
  332         struct pcm_channel *c;
  333         unsigned int encoding;
  334         int dir;
  335 
  336         dir = play ? PCMDIR_PLAY : PCMDIR_REC;
  337 
  338         if (play && d->pvchancount > 0) {
  339                 *min_rate = *max_rate = d->pvchanrate;
  340                 *fmts = AFMT_ENCODING(d->pvchanformat);
  341                 *minchn = *maxchn = AFMT_CHANNEL(d->pvchanformat);
  342                 return;
  343         } else if (!play && d->rvchancount > 0) {
  344                 *min_rate = *max_rate = d->rvchanrate;
  345                 *fmts = AFMT_ENCODING(d->rvchanformat);
  346                 *minchn = *maxchn = AFMT_CHANNEL(d->rvchanformat);
  347                 return;
  348         }
  349 
  350         *min_rate = UINT32_MAX;
  351         *max_rate = 0;
  352         *minchn = UINT32_MAX;
  353         *maxchn = 0;
  354         encoding = 0;
  355         CHN_FOREACH(c, d, channels.pcm) {
  356                 struct pcmchan_caps *caps;
  357                 int i;
  358 
  359                 if (c->direction != dir || (c->flags & CHN_F_VIRTUAL) != 0)
  360                         continue;
  361 
  362                 CHN_LOCK(c);
  363                 caps = chn_getcaps(c);
  364                 *min_rate = min(caps->minspeed, *min_rate);
  365                 *max_rate = max(caps->maxspeed, *max_rate);
  366                 for (i = 0; caps->fmtlist[i]; i++) {
  367                         encoding |= AFMT_ENCODING(caps->fmtlist[i]);
  368                         *minchn = min(AFMT_CHANNEL(encoding), *minchn);
  369                         *maxchn = max(AFMT_CHANNEL(encoding), *maxchn);
  370                 }
  371                 CHN_UNLOCK(c);
  372         }
  373         if (*min_rate == UINT32_MAX)
  374                 *min_rate = 0;
  375         if (*minchn == UINT32_MAX)
  376                 *minchn = 0;
  377 }
  378 
  379 static nvlist_t *
  380 sndstat_create_diinfo_nv(uint32_t min_rate, uint32_t max_rate, uint32_t formats,
  381             uint32_t min_chn, uint32_t max_chn)
  382 {
  383         nvlist_t *nv;
  384 
  385         nv = nvlist_create(0);
  386         if (nv == NULL)
  387                 return (NULL);
  388         nvlist_add_number(nv, SNDST_DSPS_INFO_MIN_RATE, min_rate);
  389         nvlist_add_number(nv, SNDST_DSPS_INFO_MAX_RATE, max_rate);
  390         nvlist_add_number(nv, SNDST_DSPS_INFO_FORMATS, formats);
  391         nvlist_add_number(nv, SNDST_DSPS_INFO_MIN_CHN, min_chn);
  392         nvlist_add_number(nv, SNDST_DSPS_INFO_MAX_CHN, max_chn);
  393         return (nv);
  394 }
  395 
  396 static int
  397 sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
  398 {
  399         uint32_t maxrate, minrate, fmts, minchn, maxchn;
  400         nvlist_t *di = NULL, *sound4di = NULL, *diinfo = NULL;
  401         int err;
  402 
  403         di = nvlist_create(0);
  404         if (di == NULL) {
  405                 err = ENOMEM;
  406                 goto done;
  407         }
  408         sound4di = nvlist_create(0);
  409         if (sound4di == NULL) {
  410                 err = ENOMEM;
  411                 goto done;
  412         }
  413 
  414         nvlist_add_bool(di, SNDST_DSPS_FROM_USER, false);
  415         nvlist_add_stringf(di, SNDST_DSPS_NAMEUNIT, "%s",
  416                         device_get_nameunit(d->dev));
  417         nvlist_add_stringf(di, SNDST_DSPS_DEVNODE, "dsp%d",
  418                         device_get_unit(d->dev));
  419         nvlist_add_string(
  420                         di, SNDST_DSPS_DESC, device_get_desc(d->dev));
  421 
  422         PCM_ACQUIRE_QUICK(d);
  423         nvlist_add_number(di, SNDST_DSPS_PCHAN, d->playcount);
  424         nvlist_add_number(di, SNDST_DSPS_RCHAN, d->reccount);
  425         if (d->playcount > 0) {
  426                 sndstat_get_caps(d, true, &minrate, &maxrate, &fmts, &minchn,
  427                     &maxchn);
  428                 nvlist_add_number(di, "pminrate", minrate);
  429                 nvlist_add_number(di, "pmaxrate", maxrate);
  430                 nvlist_add_number(di, "pfmts", fmts);
  431                 diinfo = sndstat_create_diinfo_nv(minrate, maxrate, fmts,
  432                     minchn, maxchn);
  433                 if (diinfo == NULL)
  434                         nvlist_set_error(di, ENOMEM);
  435                 else
  436                         nvlist_move_nvlist(di, SNDST_DSPS_INFO_PLAY, diinfo);
  437         }
  438         if (d->reccount > 0) {
  439                 sndstat_get_caps(d, false, &minrate, &maxrate, &fmts, &minchn,
  440                     &maxchn);
  441                 nvlist_add_number(di, "rminrate", minrate);
  442                 nvlist_add_number(di, "rmaxrate", maxrate);
  443                 nvlist_add_number(di, "rfmts", fmts);
  444                 diinfo = sndstat_create_diinfo_nv(minrate, maxrate, fmts,
  445                     minchn, maxchn);
  446                 if (diinfo == NULL)
  447                         nvlist_set_error(di, ENOMEM);
  448                 else
  449                         nvlist_move_nvlist(di, SNDST_DSPS_INFO_REC, diinfo);
  450         }
  451 
  452         nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_UNIT,
  453                         device_get_unit(d->dev)); // XXX: I want signed integer here
  454         nvlist_add_bool(
  455             sound4di, SNDST_DSPS_SOUND4_BITPERFECT, d->flags & SD_F_BITPERFECT);
  456         nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_PVCHAN, d->pvchancount);
  457         nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_RVCHAN, d->rvchancount);
  458         nvlist_move_nvlist(di, SNDST_DSPS_PROVIDER_INFO, sound4di);
  459         sound4di = NULL;
  460         PCM_RELEASE_QUICK(d);
  461         nvlist_add_string(di, SNDST_DSPS_PROVIDER, SNDST_DSPS_SOUND4_PROVIDER);
  462 
  463         err = nvlist_error(di);
  464         if (err)
  465                 goto done;
  466 
  467         *dip = di;
  468 
  469 done:
  470         if (err) {
  471                 nvlist_destroy(sound4di);
  472                 nvlist_destroy(di);
  473         }
  474         return (err);
  475 }
  476 
  477 static int
  478 sndstat_build_userland_nvlist(struct sndstat_userdev *ud, nvlist_t **dip)
  479 {
  480         nvlist_t *di, *diinfo;
  481         int err;
  482 
  483         di = nvlist_create(0);
  484         if (di == NULL) {
  485                 err = ENOMEM;
  486                 goto done;
  487         }
  488 
  489         nvlist_add_bool(di, SNDST_DSPS_FROM_USER, true);
  490         nvlist_add_number(di, SNDST_DSPS_PCHAN, ud->pchan);
  491         nvlist_add_number(di, SNDST_DSPS_RCHAN, ud->rchan);
  492         nvlist_add_string(di, SNDST_DSPS_NAMEUNIT, ud->nameunit);
  493         nvlist_add_string(
  494                         di, SNDST_DSPS_DEVNODE, ud->devnode);
  495         nvlist_add_string(di, SNDST_DSPS_DESC, ud->desc);
  496         if (ud->pchan != 0) {
  497                 nvlist_add_number(di, "pminrate",
  498                     ud->info_play.min_rate);
  499                 nvlist_add_number(di, "pmaxrate",
  500                     ud->info_play.max_rate);
  501                 nvlist_add_number(di, "pfmts",
  502                     ud->info_play.formats);
  503                 diinfo = sndstat_create_diinfo_nv(ud->info_play.min_rate,
  504                     ud->info_play.max_rate, ud->info_play.formats,
  505                     ud->info_play.min_chn, ud->info_play.max_chn);
  506                 if (diinfo == NULL)
  507                         nvlist_set_error(di, ENOMEM);
  508                 else
  509                         nvlist_move_nvlist(di, SNDST_DSPS_INFO_PLAY, diinfo);
  510         }
  511         if (ud->rchan != 0) {
  512                 nvlist_add_number(di, "rminrate",
  513                     ud->info_rec.min_rate);
  514                 nvlist_add_number(di, "rmaxrate",
  515                     ud->info_rec.max_rate);
  516                 nvlist_add_number(di, "rfmts",
  517                     ud->info_rec.formats);
  518                 diinfo = sndstat_create_diinfo_nv(ud->info_rec.min_rate,
  519                     ud->info_rec.max_rate, ud->info_rec.formats,
  520                     ud->info_rec.min_chn, ud->info_rec.max_chn);
  521                 if (diinfo == NULL)
  522                         nvlist_set_error(di, ENOMEM);
  523                 else
  524                         nvlist_move_nvlist(di, SNDST_DSPS_INFO_REC, diinfo);
  525         }
  526         nvlist_add_string(di, SNDST_DSPS_PROVIDER,
  527             (ud->provider != NULL) ? ud->provider : "");
  528         if (ud->provider_nvl != NULL)
  529                 nvlist_add_nvlist(
  530                     di, SNDST_DSPS_PROVIDER_INFO, ud->provider_nvl);
  531 
  532         err = nvlist_error(di);
  533         if (err)
  534                 goto done;
  535 
  536         *dip = di;
  537 
  538 done:
  539         if (err)
  540                 nvlist_destroy(di);
  541         return (err);
  542 }
  543 
  544 /*
  545  * Should only be called with the following locks held:
  546  * * sndstat_lock
  547  */
  548 static int
  549 sndstat_create_devs_nvlist(nvlist_t **nvlp)
  550 {
  551         int err;
  552         nvlist_t *nvl;
  553         struct sndstat_entry *ent;
  554         struct sndstat_file *pf;
  555 
  556         nvl = nvlist_create(0);
  557         if (nvl == NULL)
  558                 return (ENOMEM);
  559 
  560         TAILQ_FOREACH(ent, &sndstat_devlist, link) {
  561                 struct snddev_info *d;
  562                 nvlist_t *di;
  563 
  564                 if (ent->dev == NULL)
  565                         continue;
  566                 d = device_get_softc(ent->dev);
  567                 if (!PCM_REGISTERED(d))
  568                         continue;
  569 
  570                 err = sndstat_build_sound4_nvlist(d, &di);
  571                 if (err)
  572                         goto done;
  573 
  574                 nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
  575                 nvlist_destroy(di);
  576                 err = nvlist_error(nvl);
  577                 if (err)
  578                         goto done;
  579         }
  580 
  581         TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
  582                 struct sndstat_userdev *ud;
  583 
  584                 sx_xlock(&pf->lock);
  585 
  586                 TAILQ_FOREACH(ud, &pf->userdev_list, link) {
  587                         nvlist_t *di;
  588 
  589                         err = sndstat_build_userland_nvlist(ud, &di);
  590                         if (err != 0) {
  591                                 sx_xunlock(&pf->lock);
  592                                 goto done;
  593                         }
  594                         nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
  595                         nvlist_destroy(di);
  596 
  597                         err = nvlist_error(nvl);
  598                         if (err != 0) {
  599                                 sx_xunlock(&pf->lock);
  600                                 goto done;
  601                         }
  602                 }
  603 
  604                 sx_xunlock(&pf->lock);
  605         }
  606 
  607         *nvlp = nvl;
  608 
  609 done:
  610         if (err != 0)
  611                 nvlist_destroy(nvl);
  612         return (err);
  613 }
  614 
  615 static int
  616 sndstat_refresh_devs(struct sndstat_file *pf)
  617 {
  618         sx_xlock(&pf->lock);
  619         free(pf->devs_nvlbuf, M_NVLIST);
  620         pf->devs_nvlbuf = NULL;
  621         pf->devs_nbytes = 0;
  622         sx_unlock(&pf->lock);
  623 
  624         return (0);
  625 }
  626 
  627 static int
  628 sndstat_get_devs(struct sndstat_file *pf, caddr_t data)
  629 {
  630         int err;
  631         struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data;
  632 
  633         SNDSTAT_LOCK();
  634         sx_xlock(&pf->lock);
  635 
  636         if (pf->devs_nvlbuf == NULL) {
  637                 nvlist_t *nvl;
  638                 void *nvlbuf;
  639                 size_t nbytes;
  640                 int err;
  641 
  642                 sx_xunlock(&pf->lock);
  643 
  644                 err = sndstat_create_devs_nvlist(&nvl);
  645                 if (err) {
  646                         SNDSTAT_UNLOCK();
  647                         return (err);
  648                 }
  649 
  650                 sx_xlock(&pf->lock);
  651 
  652                 nvlbuf = nvlist_pack(nvl, &nbytes);
  653                 err = nvlist_error(nvl);
  654                 nvlist_destroy(nvl);
  655                 if (nvlbuf == NULL || err != 0) {
  656                         SNDSTAT_UNLOCK();
  657                         sx_xunlock(&pf->lock);
  658                         if (err == 0)
  659                                 return (ENOMEM);
  660                         return (err);
  661                 }
  662 
  663                 free(pf->devs_nvlbuf, M_NVLIST);
  664                 pf->devs_nvlbuf = nvlbuf;
  665                 pf->devs_nbytes = nbytes;
  666         }
  667 
  668         SNDSTAT_UNLOCK();
  669 
  670         if (!arg->nbytes) {
  671                 arg->nbytes = pf->devs_nbytes;
  672                 err = 0;
  673                 goto done;
  674         }
  675         if (arg->nbytes < pf->devs_nbytes) {
  676                 arg->nbytes = 0;
  677                 err = 0;
  678                 goto done;
  679         }
  680 
  681         err = copyout(pf->devs_nvlbuf, arg->buf, pf->devs_nbytes);
  682         if (err)
  683                 goto done;
  684 
  685         arg->nbytes = pf->devs_nbytes;
  686 
  687         free(pf->devs_nvlbuf, M_NVLIST);
  688         pf->devs_nvlbuf = NULL;
  689         pf->devs_nbytes = 0;
  690 
  691 done:
  692         sx_unlock(&pf->lock);
  693         return (err);
  694 }
  695 
  696 static int
  697 sndstat_unpack_user_nvlbuf(const void *unvlbuf, size_t nbytes, nvlist_t **nvl)
  698 {
  699         void *nvlbuf;
  700         int err;
  701 
  702         nvlbuf = malloc(nbytes, M_DEVBUF, M_WAITOK);
  703         err = copyin(unvlbuf, nvlbuf, nbytes);
  704         if (err != 0) {
  705                 free(nvlbuf, M_DEVBUF);
  706                 return (err);
  707         }
  708         *nvl = nvlist_unpack(nvlbuf, nbytes, 0);
  709         free(nvlbuf, M_DEVBUF);
  710         if (nvl == NULL) {
  711                 return (EINVAL);
  712         }
  713 
  714         return (0);
  715 }
  716 
  717 static bool
  718 sndstat_diinfo_is_sane(const nvlist_t *diinfo)
  719 {
  720         if (!(nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_RATE) &&
  721             nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_RATE) &&
  722             nvlist_exists_number(diinfo, SNDST_DSPS_INFO_FORMATS) &&
  723             nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_CHN) &&
  724             nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_CHN)))
  725                 return (false);
  726         return (true);
  727 }
  728 
  729 static bool
  730 sndstat_dsp_nvlist_is_sane(const nvlist_t *nvlist)
  731 {
  732         if (!(nvlist_exists_string(nvlist, SNDST_DSPS_DEVNODE) &&
  733             nvlist_exists_string(nvlist, SNDST_DSPS_DESC) &&
  734             nvlist_exists_number(nvlist, SNDST_DSPS_PCHAN) &&
  735             nvlist_exists_number(nvlist, SNDST_DSPS_RCHAN)))
  736                 return (false);
  737 
  738         if (nvlist_get_number(nvlist, SNDST_DSPS_PCHAN) > 0) {
  739                 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) {
  740                         if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist,
  741                             SNDST_DSPS_INFO_PLAY)))
  742                                 return (false);
  743                 } else if (!(nvlist_exists_number(nvlist, "pminrate") &&
  744                     nvlist_exists_number(nvlist, "pmaxrate") &&
  745                     nvlist_exists_number(nvlist, "pfmts")))
  746                         return (false);
  747         }
  748 
  749         if (nvlist_get_number(nvlist, SNDST_DSPS_RCHAN) > 0) {
  750                 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) {
  751                         if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist,
  752                             SNDST_DSPS_INFO_REC)))
  753                                 return (false);
  754                 } else if (!(nvlist_exists_number(nvlist, "rminrate") &&
  755                     nvlist_exists_number(nvlist, "rmaxrate") &&
  756                     nvlist_exists_number(nvlist, "rfmts")))
  757                         return (false);
  758         }
  759         
  760         return (true);
  761 
  762 }
  763 
  764 static void
  765 sndstat_get_diinfo_nv(const nvlist_t *nv, uint32_t *min_rate,
  766             uint32_t *max_rate, uint32_t *formats, uint32_t *min_chn,
  767             uint32_t *max_chn)
  768 {
  769         *min_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_RATE);
  770         *max_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_RATE);
  771         *formats = nvlist_get_number(nv, SNDST_DSPS_INFO_FORMATS);
  772         *min_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_CHN);
  773         *max_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_CHN);
  774 }
  775 
  776 static int
  777 sndstat_dsp_unpack_nvlist(const nvlist_t *nvlist, struct sndstat_userdev *ud)
  778 {
  779         const char *nameunit, *devnode, *desc;
  780         unsigned int pchan, rchan;
  781         uint32_t pminrate = 0, pmaxrate = 0;
  782         uint32_t rminrate = 0, rmaxrate = 0;
  783         uint32_t pfmts = 0, rfmts = 0;
  784         uint32_t pminchn = 0, pmaxchn = 0;
  785         uint32_t rminchn = 0, rmaxchn = 0;
  786         nvlist_t *provider_nvl = NULL;
  787         const nvlist_t *diinfo;
  788         const char *provider;
  789 
  790         devnode = nvlist_get_string(nvlist, SNDST_DSPS_DEVNODE);
  791         if (nvlist_exists_string(nvlist, SNDST_DSPS_NAMEUNIT))
  792                 nameunit = nvlist_get_string(nvlist, SNDST_DSPS_NAMEUNIT);
  793         else
  794                 nameunit = devnode;
  795         desc = nvlist_get_string(nvlist, SNDST_DSPS_DESC);
  796         pchan = nvlist_get_number(nvlist, SNDST_DSPS_PCHAN);
  797         rchan = nvlist_get_number(nvlist, SNDST_DSPS_RCHAN);
  798         if (pchan != 0) {
  799                 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) {
  800                         diinfo = nvlist_get_nvlist(nvlist,
  801                             SNDST_DSPS_INFO_PLAY);
  802                         sndstat_get_diinfo_nv(diinfo, &pminrate, &pmaxrate,
  803                             &pfmts, &pminchn, &pmaxchn);
  804                 } else {
  805                         pminrate = nvlist_get_number(nvlist, "pminrate");
  806                         pmaxrate = nvlist_get_number(nvlist, "pmaxrate");
  807                         pfmts = nvlist_get_number(nvlist, "pfmts");
  808                 }
  809         }
  810         if (rchan != 0) {
  811                 if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) {
  812                         diinfo = nvlist_get_nvlist(nvlist,
  813                             SNDST_DSPS_INFO_REC);
  814                         sndstat_get_diinfo_nv(diinfo, &rminrate, &rmaxrate,
  815                             &rfmts, &rminchn, &rmaxchn);
  816                 } else {
  817                         rminrate = nvlist_get_number(nvlist, "rminrate");
  818                         rmaxrate = nvlist_get_number(nvlist, "rmaxrate");
  819                         rfmts = nvlist_get_number(nvlist, "rfmts");
  820                 }
  821         }
  822 
  823         provider = dnvlist_get_string(nvlist, SNDST_DSPS_PROVIDER, "");
  824         if (provider[0] == '\0')
  825                 provider = NULL;
  826 
  827         if (provider != NULL &&
  828             nvlist_exists_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO)) {
  829                 provider_nvl = nvlist_clone(
  830                     nvlist_get_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO));
  831                 if (provider_nvl == NULL)
  832                         return (ENOMEM);
  833         }
  834 
  835         ud->provider = (provider != NULL) ? strdup(provider, M_DEVBUF) : NULL;
  836         ud->devnode = strdup(devnode, M_DEVBUF);
  837         ud->nameunit = strdup(nameunit, M_DEVBUF);
  838         ud->desc = strdup(desc, M_DEVBUF);
  839         ud->pchan = pchan;
  840         ud->rchan = rchan;
  841         ud->info_play.min_rate = pminrate;
  842         ud->info_play.max_rate = pmaxrate;
  843         ud->info_play.formats = pfmts;
  844         ud->info_play.min_chn = pminchn;
  845         ud->info_play.max_chn = pmaxchn;
  846         ud->info_rec.min_rate = rminrate;
  847         ud->info_rec.max_rate = rmaxrate;
  848         ud->info_rec.formats = rfmts;
  849         ud->info_rec.min_chn = rminchn;
  850         ud->info_rec.max_chn = rmaxchn;
  851         ud->provider_nvl = provider_nvl;
  852         return (0);
  853 }
  854 
  855 static int
  856 sndstat_add_user_devs(struct sndstat_file *pf, caddr_t data)
  857 {
  858         int err;
  859         nvlist_t *nvl = NULL;
  860         const nvlist_t * const *dsps;
  861         size_t i, ndsps;
  862         struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data;
  863 
  864         if ((pf->fflags & FWRITE) == 0) {
  865                 err = EPERM;
  866                 goto done;
  867         }
  868 
  869         err = sndstat_unpack_user_nvlbuf(arg->buf, arg->nbytes, &nvl);
  870         if (err != 0)
  871                 goto done;
  872 
  873         if (!nvlist_exists_nvlist_array(nvl, SNDST_DSPS)) {
  874                 err = EINVAL;
  875                 goto done;
  876         }
  877         dsps = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &ndsps);
  878         for (i = 0; i < ndsps; i++) {
  879                 if (!sndstat_dsp_nvlist_is_sane(dsps[i])) {
  880                         err = EINVAL;
  881                         goto done;
  882                 }
  883         }
  884         sx_xlock(&pf->lock);
  885         for (i = 0; i < ndsps; i++) {
  886                 struct sndstat_userdev *ud =
  887                     malloc(sizeof(*ud), M_DEVBUF, M_WAITOK);
  888                 err = sndstat_dsp_unpack_nvlist(dsps[i], ud);
  889                 if (err) {
  890                         sx_unlock(&pf->lock);
  891                         goto done;
  892                 }
  893                 TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link);
  894         }
  895         sx_unlock(&pf->lock);
  896 
  897 done:
  898         nvlist_destroy(nvl);
  899         return (err);
  900 }
  901 
  902 static int
  903 sndstat_flush_user_devs(struct sndstat_file *pf)
  904 {
  905         if ((pf->fflags & FWRITE) == 0)
  906                 return (EPERM);
  907 
  908         sx_xlock(&pf->lock);
  909         sndstat_remove_all_userdevs(pf);
  910         sx_xunlock(&pf->lock);
  911 
  912         return (0);
  913 }
  914 
  915 #ifdef COMPAT_FREEBSD32
  916 static int
  917 compat_sndstat_get_devs32(struct sndstat_file *pf, caddr_t data)
  918 {
  919         struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data;
  920         struct sndstioc_nv_arg arg;
  921         int err;
  922 
  923         arg.buf = (void *)(uintptr_t)arg32->buf;
  924         arg.nbytes = arg32->nbytes;
  925 
  926         err = sndstat_get_devs(pf, (caddr_t)&arg);
  927         if (err == 0) {
  928                 arg32->buf = (uint32_t)(uintptr_t)arg.buf;
  929                 arg32->nbytes = arg.nbytes;
  930         }
  931 
  932         return (err);
  933 }
  934 
  935 static int
  936 compat_sndstat_add_user_devs32(struct sndstat_file *pf, caddr_t data)
  937 {
  938         struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data;
  939         struct sndstioc_nv_arg arg;
  940         int err;
  941 
  942         arg.buf = (void *)(uintptr_t)arg32->buf;
  943         arg.nbytes = arg32->nbytes;
  944 
  945         err = sndstat_add_user_devs(pf, (caddr_t)&arg);
  946         if (err == 0) {
  947                 arg32->buf = (uint32_t)(uintptr_t)arg.buf;
  948                 arg32->nbytes = arg.nbytes;
  949         }
  950 
  951         return (err);
  952 }
  953 #endif
  954 
  955 static int
  956 sndstat_ioctl(
  957     struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
  958 {
  959         int err;
  960         struct sndstat_file *pf;
  961 
  962         err = devfs_get_cdevpriv((void **)&pf);
  963         if (err != 0)
  964                 return (err);
  965 
  966         switch (cmd) {
  967         case SNDSTIOC_GET_DEVS:
  968                 err = sndstat_get_devs(pf, data);
  969                 break;
  970 #ifdef COMPAT_FREEBSD32
  971         case SNDSTIOC_GET_DEVS32:
  972                 if (!SV_CURPROC_FLAG(SV_ILP32)) {
  973                         err = ENODEV;
  974                         break;
  975                 }
  976                 err = compat_sndstat_get_devs32(pf, data);
  977                 break;
  978 #endif
  979         case SNDSTIOC_ADD_USER_DEVS:
  980                 err = sndstat_add_user_devs(pf, data);
  981                 break;
  982 #ifdef COMPAT_FREEBSD32
  983         case SNDSTIOC_ADD_USER_DEVS32:
  984                 if (!SV_CURPROC_FLAG(SV_ILP32)) {
  985                         err = ENODEV;
  986                         break;
  987                 }
  988                 err = compat_sndstat_add_user_devs32(pf, data);
  989                 break;
  990 #endif
  991         case SNDSTIOC_REFRESH_DEVS:
  992                 err = sndstat_refresh_devs(pf);
  993                 break;
  994         case SNDSTIOC_FLUSH_USER_DEVS:
  995                 err = sndstat_flush_user_devs(pf);
  996                 break;
  997         default:
  998                 err = ENODEV;
  999         }
 1000 
 1001         return (err);
 1002 }
 1003 
 1004 static struct sndstat_userdev *
 1005 sndstat_line2userdev(struct sndstat_file *pf, const char *line, int n)
 1006 {
 1007         struct sndstat_userdev *ud;
 1008         const char *e, *m;
 1009 
 1010         ud = malloc(sizeof(*ud), M_DEVBUF, M_WAITOK|M_ZERO);
 1011 
 1012         ud->provider = NULL;
 1013         ud->provider_nvl = NULL;
 1014         e = strchr(line, ':');
 1015         if (e == NULL)
 1016                 goto fail;
 1017         ud->nameunit = strndup(line, e - line, M_DEVBUF);
 1018         ud->devnode = (char *)malloc(e - line + 1, M_DEVBUF, M_WAITOK | M_ZERO);
 1019         strlcat(ud->devnode, ud->nameunit, e - line + 1);
 1020         line = e + 1;
 1021 
 1022         e = strchr(line, '<');
 1023         if (e == NULL)
 1024                 goto fail;
 1025         line = e + 1;
 1026         e = strrchr(line, '>');
 1027         if (e == NULL)
 1028                 goto fail;
 1029         ud->desc = strndup(line, e - line, M_DEVBUF);
 1030         line = e + 1;
 1031 
 1032         e = strchr(line, '(');
 1033         if (e == NULL)
 1034                 goto fail;
 1035         line = e + 1;
 1036         e = strrchr(line, ')');
 1037         if (e == NULL)
 1038                 goto fail;
 1039         m = strstr(line, "play");
 1040         if (m != NULL && m < e)
 1041                 ud->pchan = 1;
 1042         m = strstr(line, "rec");
 1043         if (m != NULL && m < e)
 1044                 ud->rchan = 1;
 1045 
 1046         return (ud);
 1047 
 1048 fail:
 1049         free(ud->nameunit, M_DEVBUF);
 1050         free(ud->devnode, M_DEVBUF);
 1051         free(ud->desc, M_DEVBUF);
 1052         free(ud, M_DEVBUF);
 1053         return (NULL);
 1054 }
 1055 
 1056 /************************************************************************/
 1057 
 1058 int
 1059 sndstat_register(device_t dev, char *str, sndstat_handler handler)
 1060 {
 1061         struct sndstat_entry *ent;
 1062         struct sndstat_entry *pre;
 1063         const char *devtype;
 1064         int type, unit;
 1065 
 1066         if (dev) {
 1067                 unit = device_get_unit(dev);
 1068                 devtype = device_get_name(dev);
 1069                 if (!strcmp(devtype, "pcm"))
 1070                         type = SS_TYPE_PCM;
 1071                 else if (!strcmp(devtype, "midi"))
 1072                         type = SS_TYPE_MIDI;
 1073                 else if (!strcmp(devtype, "sequencer"))
 1074                         type = SS_TYPE_SEQUENCER;
 1075                 else
 1076                         return (EINVAL);
 1077         } else {
 1078                 type = SS_TYPE_MODULE;
 1079                 unit = -1;
 1080         }
 1081 
 1082         ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO);
 1083         ent->dev = dev;
 1084         ent->str = str;
 1085         ent->type = type;
 1086         ent->unit = unit;
 1087         ent->handler = handler;
 1088 
 1089         SNDSTAT_LOCK();
 1090         /* sorted list insertion */
 1091         TAILQ_FOREACH(pre, &sndstat_devlist, link) {
 1092                 if (pre->unit > unit)
 1093                         break;
 1094                 else if (pre->unit < unit)
 1095                         continue;
 1096                 if (pre->type > type)
 1097                         break;
 1098                 else if (pre->type < unit)
 1099                         continue;
 1100         }
 1101         if (pre == NULL) {
 1102                 TAILQ_INSERT_TAIL(&sndstat_devlist, ent, link);
 1103         } else {
 1104                 TAILQ_INSERT_BEFORE(pre, ent, link);
 1105         }
 1106         SNDSTAT_UNLOCK();
 1107 
 1108         return (0);
 1109 }
 1110 
 1111 int
 1112 sndstat_registerfile(char *str)
 1113 {
 1114         return (sndstat_register(NULL, str, NULL));
 1115 }
 1116 
 1117 int
 1118 sndstat_unregister(device_t dev)
 1119 {
 1120         struct sndstat_entry *ent;
 1121         int error = ENXIO;
 1122 
 1123         SNDSTAT_LOCK();
 1124         TAILQ_FOREACH(ent, &sndstat_devlist, link) {
 1125                 if (ent->dev == dev) {
 1126                         TAILQ_REMOVE(&sndstat_devlist, ent, link);
 1127                         free(ent, M_DEVBUF);
 1128                         error = 0;
 1129                         break;
 1130                 }
 1131         }
 1132         SNDSTAT_UNLOCK();
 1133 
 1134         return (error);
 1135 }
 1136 
 1137 int
 1138 sndstat_unregisterfile(char *str)
 1139 {
 1140         struct sndstat_entry *ent;
 1141         int error = ENXIO;
 1142 
 1143         SNDSTAT_LOCK();
 1144         TAILQ_FOREACH(ent, &sndstat_devlist, link) {
 1145                 if (ent->dev == NULL && ent->str == str) {
 1146                         TAILQ_REMOVE(&sndstat_devlist, ent, link);
 1147                         free(ent, M_DEVBUF);
 1148                         error = 0;
 1149                         break;
 1150                 }
 1151         }
 1152         SNDSTAT_UNLOCK();
 1153 
 1154         return (error);
 1155 }
 1156 
 1157 /************************************************************************/
 1158 
 1159 static int
 1160 sndstat_prepare(struct sndstat_file *pf_self)
 1161 {
 1162         struct sbuf *s = &pf_self->sbuf;
 1163         struct sndstat_entry *ent;
 1164         struct snddev_info *d;
 1165         struct sndstat_file *pf;
 1166         int k;
 1167 
 1168         /* make sure buffer is reset */
 1169         sbuf_clear(s);
 1170 
 1171         if (snd_verbose > 0) {
 1172                 sbuf_printf(s, "FreeBSD Audio Driver (%ubit %d/%s)\n",
 1173                     (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION,
 1174                     MACHINE_ARCH);
 1175         }
 1176 
 1177         /* generate list of installed devices */
 1178         k = 0;
 1179         TAILQ_FOREACH(ent, &sndstat_devlist, link) {
 1180                 if (ent->dev == NULL)
 1181                         continue;
 1182                 d = device_get_softc(ent->dev);
 1183                 if (!PCM_REGISTERED(d))
 1184                         continue;
 1185                 if (!k++)
 1186                         sbuf_printf(s, "Installed devices:\n");
 1187                 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
 1188                 sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
 1189                 if (snd_verbose > 0)
 1190                         sbuf_printf(s, " %s", ent->str);
 1191                 if (ent->handler) {
 1192                         /* XXX Need Giant magic entry ??? */
 1193                         PCM_ACQUIRE_QUICK(d);
 1194                         ent->handler(s, ent->dev, snd_verbose);
 1195                         PCM_RELEASE_QUICK(d);
 1196                 }
 1197                 sbuf_printf(s, "\n");
 1198         }
 1199         if (k == 0)
 1200                 sbuf_printf(s, "No devices installed.\n");
 1201 
 1202         /* append any input from userspace */
 1203         k = 0;
 1204         TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
 1205                 struct sndstat_userdev *ud;
 1206 
 1207                 if (pf == pf_self)
 1208                         continue;
 1209                 sx_xlock(&pf->lock);
 1210                 if (TAILQ_EMPTY(&pf->userdev_list)) {
 1211                         sx_unlock(&pf->lock);
 1212                         continue;
 1213                 }
 1214                 if (!k++)
 1215                         sbuf_printf(s, "Installed devices from userspace:\n");
 1216                 TAILQ_FOREACH(ud, &pf->userdev_list, link) {
 1217                         const char *caps = (ud->pchan && ud->rchan) ?
 1218                             "play/rec" :
 1219                             (ud->pchan ? "play" : (ud->rchan ? "rec" : ""));
 1220                         sbuf_printf(s, "%s: <%s>", ud->nameunit, ud->desc);
 1221                         sbuf_printf(s, " (%s)", caps);
 1222                         sbuf_printf(s, "\n");
 1223                 }
 1224                 sx_unlock(&pf->lock);
 1225         }
 1226         if (k == 0)
 1227                 sbuf_printf(s, "No devices installed from userspace.\n");
 1228 
 1229         /* append any file versions */
 1230         if (snd_verbose >= 3) {
 1231                 k = 0;
 1232                 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
 1233                         if (ent->dev == NULL && ent->str != NULL) {
 1234                                 if (!k++)
 1235                                         sbuf_printf(s, "\nFile Versions:\n");
 1236                                 sbuf_printf(s, "%s\n", ent->str);
 1237                         }
 1238                 }
 1239                 if (k == 0)
 1240                         sbuf_printf(s, "\nNo file versions.\n");
 1241         }
 1242         sbuf_finish(s);
 1243         return (sbuf_len(s));
 1244 }
 1245 
 1246 static void
 1247 sndstat_sysinit(void *p)
 1248 {
 1249         sx_init(&sndstat_lock, "sndstat lock");
 1250         sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS,
 1251             UID_ROOT, GID_WHEEL, 0644, "sndstat");
 1252 }
 1253 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
 1254 
 1255 static void
 1256 sndstat_sysuninit(void *p)
 1257 {
 1258         if (sndstat_dev != NULL) {
 1259                 /* destroy_dev() will wait for all references to go away */
 1260                 destroy_dev(sndstat_dev);
 1261         }
 1262         sx_destroy(&sndstat_lock);
 1263 }
 1264 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);

Cache object: ac9ea4c51ef5478ade31b416b6d78d79


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