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  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #ifdef HAVE_KERNEL_OPTION_HEADERS
   31 #include "opt_snd.h"
   32 #endif
   33 
   34 #include <dev/sound/pcm/sound.h>
   35 #include <dev/sound/pcm/pcm.h>
   36 #include <dev/sound/version.h>
   37 #include <sys/sx.h>
   38 
   39 SND_DECLARE_FILE("$FreeBSD$");
   40 
   41 #define SS_TYPE_MODULE          0
   42 #define SS_TYPE_PCM             1
   43 #define SS_TYPE_MIDI            2
   44 #define SS_TYPE_SEQUENCER       3
   45 
   46 static d_open_t sndstat_open;
   47 static void sndstat_close(void *);
   48 static d_read_t sndstat_read;
   49 static d_write_t sndstat_write;
   50 
   51 static struct cdevsw sndstat_cdevsw = {
   52         .d_version =    D_VERSION,
   53         .d_open =       sndstat_open,
   54         .d_read =       sndstat_read,
   55         .d_write =      sndstat_write,
   56         .d_name =       "sndstat",
   57         .d_flags =      D_TRACKCLOSE,
   58 };
   59 
   60 struct sndstat_entry {
   61         TAILQ_ENTRY(sndstat_entry) link;
   62         device_t dev;
   63         char *str;
   64         sndstat_handler handler;
   65         int type, unit;
   66 };
   67 
   68 struct sndstat_file {
   69         TAILQ_ENTRY(sndstat_file) entry;
   70         struct sbuf sbuf;
   71         int out_offset;
   72         int in_offset;
   73 };
   74 
   75 static struct sx sndstat_lock;
   76 static struct cdev *sndstat_dev;
   77 
   78 #define SNDSTAT_LOCK() sx_xlock(&sndstat_lock)
   79 #define SNDSTAT_UNLOCK() sx_xunlock(&sndstat_lock)
   80 
   81 static TAILQ_HEAD(, sndstat_entry) sndstat_devlist = TAILQ_HEAD_INITIALIZER(sndstat_devlist);
   82 static TAILQ_HEAD(, sndstat_file) sndstat_filelist = TAILQ_HEAD_INITIALIZER(sndstat_filelist);
   83 
   84 int snd_verbose = 0;
   85 
   86 static int sndstat_prepare(struct sndstat_file *);
   87 
   88 static int
   89 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
   90 {
   91         int error, verbose;
   92 
   93         verbose = snd_verbose;
   94         error = sysctl_handle_int(oidp, &verbose, 0, req);
   95         if (error == 0 && req->newptr != NULL) {
   96                 if (verbose < 0 || verbose > 4)
   97                         error = EINVAL;
   98                 else
   99                         snd_verbose = verbose;
  100         }
  101         return (error);
  102 }
  103 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RWTUN,
  104             0, sizeof(int), sysctl_hw_sndverbose, "I", "verbosity level");
  105 
  106 static int
  107 sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
  108 {
  109         struct sndstat_file *pf;
  110 
  111         pf = malloc(sizeof(*pf), M_DEVBUF, M_WAITOK | M_ZERO);
  112 
  113         SNDSTAT_LOCK();
  114         if (sbuf_new(&pf->sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
  115                 SNDSTAT_UNLOCK();
  116                 free(pf, M_DEVBUF);
  117                 return (ENOMEM);
  118         }
  119         TAILQ_INSERT_TAIL(&sndstat_filelist, pf, entry);
  120         SNDSTAT_UNLOCK();
  121 
  122         devfs_set_cdevpriv(pf, &sndstat_close);
  123 
  124         return (0);
  125 }
  126 
  127 static void
  128 sndstat_close(void *sndstat_file)
  129 {
  130         struct sndstat_file *pf = (struct sndstat_file *)sndstat_file;
  131 
  132         SNDSTAT_LOCK();
  133         sbuf_delete(&pf->sbuf);
  134         TAILQ_REMOVE(&sndstat_filelist, pf, entry);
  135         SNDSTAT_UNLOCK();
  136 
  137         free(pf, M_DEVBUF);
  138 }
  139 
  140 static int
  141 sndstat_read(struct cdev *i_dev, struct uio *buf, int flag)
  142 {
  143         struct sndstat_file *pf;
  144         int err;
  145         int len;
  146 
  147         err = devfs_get_cdevpriv((void **)&pf);
  148         if (err != 0)
  149                 return (err);
  150 
  151         /* skip zero-length reads */
  152         if (buf->uio_resid == 0)
  153                 return (0);
  154 
  155         SNDSTAT_LOCK();
  156         if (pf->out_offset != 0) {
  157                 /* don't allow both reading and writing */
  158                 err = EINVAL;
  159                 goto done;
  160         } else if (pf->in_offset == 0) {
  161                 err = sndstat_prepare(pf);
  162                 if (err <= 0) {
  163                         err = ENOMEM;
  164                         goto done;
  165                 }
  166         }
  167         len = sbuf_len(&pf->sbuf) - pf->in_offset;
  168         if (len > buf->uio_resid)
  169                 len = buf->uio_resid;
  170         if (len > 0)
  171                 err = uiomove(sbuf_data(&pf->sbuf) + pf->in_offset, len, buf);
  172         pf->in_offset += len;
  173 done:
  174         SNDSTAT_UNLOCK();
  175         return (err);
  176 }
  177 
  178 static int
  179 sndstat_write(struct cdev *i_dev, struct uio *buf, int flag)
  180 {
  181         struct sndstat_file *pf;
  182         uint8_t temp[64];
  183         int err;
  184         int len;
  185 
  186         err = devfs_get_cdevpriv((void **)&pf);
  187         if (err != 0)
  188                 return (err);
  189 
  190         /* skip zero-length writes */
  191         if (buf->uio_resid == 0)
  192                 return (0);
  193 
  194         /* don't allow writing more than 64Kbytes */
  195         if (buf->uio_resid > 65536)
  196                 return (ENOMEM);
  197 
  198         SNDSTAT_LOCK();
  199         if (pf->in_offset != 0) {
  200                 /* don't allow both reading and writing */
  201                 err = EINVAL;
  202         } else {
  203                 /* only remember the last write - allows for updates */
  204                 sbuf_clear(&pf->sbuf);
  205                 while (1) {
  206                         len = sizeof(temp);
  207                         if (len > buf->uio_resid)
  208                                 len = buf->uio_resid;
  209                         if (len > 0) {
  210                                 err = uiomove(temp, len, buf);
  211                                 if (err)
  212                                         break;
  213                         } else {
  214                                 break;
  215                         }
  216                         if (sbuf_bcat(&pf->sbuf, temp, len) < 0) {
  217                                 err = ENOMEM;
  218                                 break;
  219                         }
  220                 }
  221                 sbuf_finish(&pf->sbuf);
  222                 if (err == 0)
  223                         pf->out_offset = sbuf_len(&pf->sbuf);
  224                 else
  225                         pf->out_offset = 0;
  226         }
  227         SNDSTAT_UNLOCK();
  228         return (err);
  229 }
  230 
  231 /************************************************************************/
  232 
  233 int
  234 sndstat_register(device_t dev, char *str, sndstat_handler handler)
  235 {
  236         struct sndstat_entry *ent;
  237         struct sndstat_entry *pre;
  238         const char *devtype;
  239         int type, unit;
  240 
  241         if (dev) {
  242                 unit = device_get_unit(dev);
  243                 devtype = device_get_name(dev);
  244                 if (!strcmp(devtype, "pcm"))
  245                         type = SS_TYPE_PCM;
  246                 else if (!strcmp(devtype, "midi"))
  247                         type = SS_TYPE_MIDI;
  248                 else if (!strcmp(devtype, "sequencer"))
  249                         type = SS_TYPE_SEQUENCER;
  250                 else
  251                         return (EINVAL);
  252         } else {
  253                 type = SS_TYPE_MODULE;
  254                 unit = -1;
  255         }
  256 
  257         ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO);
  258         ent->dev = dev;
  259         ent->str = str;
  260         ent->type = type;
  261         ent->unit = unit;
  262         ent->handler = handler;
  263 
  264         SNDSTAT_LOCK();
  265         /* sorted list insertion */
  266         TAILQ_FOREACH(pre, &sndstat_devlist, link) {
  267                 if (pre->unit > unit)
  268                         break;
  269                 else if (pre->unit < unit)
  270                         continue;
  271                 if (pre->type > type)
  272                         break;
  273                 else if (pre->type < unit)
  274                         continue;
  275         }
  276         if (pre == NULL) {
  277                 TAILQ_INSERT_TAIL(&sndstat_devlist, ent, link);
  278         } else {
  279                 TAILQ_INSERT_BEFORE(pre, ent, link);
  280         }
  281         SNDSTAT_UNLOCK();
  282 
  283         return (0);
  284 }
  285 
  286 int
  287 sndstat_registerfile(char *str)
  288 {
  289         return (sndstat_register(NULL, str, NULL));
  290 }
  291 
  292 int
  293 sndstat_unregister(device_t dev)
  294 {
  295         struct sndstat_entry *ent;
  296         int error = ENXIO;
  297 
  298         SNDSTAT_LOCK();
  299         TAILQ_FOREACH(ent, &sndstat_devlist, link) {
  300                 if (ent->dev == dev) {
  301                         TAILQ_REMOVE(&sndstat_devlist, ent, link);
  302                         free(ent, M_DEVBUF);
  303                         error = 0;
  304                         break;
  305                 }
  306         }
  307         SNDSTAT_UNLOCK();
  308 
  309         return (error);
  310 }
  311 
  312 int
  313 sndstat_unregisterfile(char *str)
  314 {
  315         struct sndstat_entry *ent;
  316         int error = ENXIO;
  317 
  318         SNDSTAT_LOCK();
  319         TAILQ_FOREACH(ent, &sndstat_devlist, link) {
  320                 if (ent->dev == NULL && ent->str == str) {
  321                         TAILQ_REMOVE(&sndstat_devlist, ent, link);
  322                         free(ent, M_DEVBUF);
  323                         error = 0;
  324                         break;
  325                 }
  326         }
  327         SNDSTAT_UNLOCK();
  328 
  329         return (error);
  330 }
  331 
  332 /************************************************************************/
  333 
  334 static int
  335 sndstat_prepare(struct sndstat_file *pf_self)
  336 {
  337         struct sbuf *s = &pf_self->sbuf;
  338         struct sndstat_entry *ent;
  339         struct snddev_info *d;
  340         struct sndstat_file *pf;
  341         int k;
  342 
  343         /* make sure buffer is reset */
  344         sbuf_clear(s);
  345         
  346         if (snd_verbose > 0) {
  347                 sbuf_printf(s, "FreeBSD Audio Driver (%ubit %d/%s)\n",
  348                     (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION,
  349                     MACHINE_ARCH);
  350         }
  351 
  352         /* generate list of installed devices */
  353         k = 0;
  354         TAILQ_FOREACH(ent, &sndstat_devlist, link) {
  355                 if (ent->dev == NULL)
  356                         continue;
  357                 d = device_get_softc(ent->dev);
  358                 if (!PCM_REGISTERED(d))
  359                         continue;
  360                 if (!k++)
  361                         sbuf_printf(s, "Installed devices:\n");
  362                 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
  363                 sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
  364                 if (snd_verbose > 0)
  365                         sbuf_printf(s, " %s", ent->str);
  366                 if (ent->handler) {
  367                         /* XXX Need Giant magic entry ??? */
  368                         PCM_ACQUIRE_QUICK(d);
  369                         ent->handler(s, ent->dev, snd_verbose);
  370                         PCM_RELEASE_QUICK(d);
  371                 }
  372                 sbuf_printf(s, "\n");
  373         }
  374         if (k == 0)
  375                 sbuf_printf(s, "No devices installed.\n");
  376 
  377         /* append any input from userspace */
  378         k = 0;
  379         TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
  380                 if (pf == pf_self)
  381                         continue;
  382                 if (pf->out_offset == 0)
  383                         continue;
  384                 if (!k++)
  385                         sbuf_printf(s, "Installed devices from userspace:\n");
  386                 sbuf_bcat(s, sbuf_data(&pf->sbuf),
  387                     sbuf_len(&pf->sbuf));
  388         }
  389         if (k == 0)
  390                 sbuf_printf(s, "No devices installed from userspace.\n");
  391 
  392         /* append any file versions */
  393         if (snd_verbose >= 3) {
  394                 k = 0;
  395                 TAILQ_FOREACH(ent, &sndstat_devlist, link) {
  396                         if (ent->dev == NULL && ent->str != NULL) {
  397                                 if (!k++)
  398                                         sbuf_printf(s, "\nFile Versions:\n");
  399                                 sbuf_printf(s, "%s\n", ent->str);
  400                         }
  401                 }
  402                 if (k == 0)
  403                         sbuf_printf(s, "\nNo file versions.\n");
  404         }
  405         sbuf_finish(s);
  406         return (sbuf_len(s));
  407 }
  408 
  409 static void
  410 sndstat_sysinit(void *p)
  411 {
  412         sx_init(&sndstat_lock, "sndstat lock");
  413         sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS,
  414             UID_ROOT, GID_WHEEL, 0644, "sndstat");
  415 }
  416 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
  417 
  418 static void
  419 sndstat_sysuninit(void *p)
  420 {
  421         if (sndstat_dev != NULL) {
  422                 /* destroy_dev() will wait for all references to go away */
  423                 destroy_dev(sndstat_dev);
  424         }
  425         sx_destroy(&sndstat_lock);
  426 }
  427 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);

Cache object: fd78bd3ff53e2f0de8c586d4089fbf4a


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