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/dsp.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) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
    3  * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
    4  * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 #ifdef HAVE_KERNEL_OPTION_HEADERS
   30 #include "opt_snd.h"
   31 #endif
   32 
   33 #include <dev/sound/pcm/sound.h>
   34 #include <sys/ctype.h>
   35 #include <sys/sysent.h>
   36 
   37 #include <vm/vm.h>
   38 #include <vm/vm_object.h>
   39 #include <vm/vm_page.h>
   40 #include <vm/vm_pager.h>
   41 
   42 SND_DECLARE_FILE("$FreeBSD: releng/8.4/sys/dev/sound/pcm/dsp.c 227106 2011-11-05 11:04:25Z avg $");
   43 
   44 static int dsp_mmap_allow_prot_exec = 0;
   45 SYSCTL_INT(_hw_snd, OID_AUTO, compat_linux_mmap, CTLFLAG_RW,
   46     &dsp_mmap_allow_prot_exec, 0,
   47     "linux mmap compatibility (-1=force disable 0=auto 1=force enable)");
   48 
   49 struct dsp_cdevinfo {
   50         struct pcm_channel *rdch, *wrch;
   51         struct pcm_channel *volch;
   52         int busy, simplex;
   53         TAILQ_ENTRY(dsp_cdevinfo) link;
   54 };
   55 
   56 #define PCM_RDCH(x)             (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch)
   57 #define PCM_WRCH(x)             (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch)
   58 #define PCM_VOLCH(x)            (((struct dsp_cdevinfo *)(x)->si_drv1)->volch)
   59 #define PCM_SIMPLEX(x)          (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex)
   60 
   61 #define DSP_CDEVINFO_CACHESIZE  8
   62 
   63 #define DSP_REGISTERED(x, y)    (PCM_REGISTERED(x) &&                   \
   64                                  (y) != NULL && (y)->si_drv1 != NULL)
   65 
   66 #define OLDPCM_IOCTL
   67 
   68 static d_open_t dsp_open;
   69 static d_close_t dsp_close;
   70 static d_read_t dsp_read;
   71 static d_write_t dsp_write;
   72 static d_ioctl_t dsp_ioctl;
   73 static d_poll_t dsp_poll;
   74 static d_mmap_t dsp_mmap;
   75 static d_mmap_single_t dsp_mmap_single;
   76 
   77 struct cdevsw dsp_cdevsw = {
   78         .d_version =    D_VERSION,
   79         .d_open =       dsp_open,
   80         .d_close =      dsp_close,
   81         .d_read =       dsp_read,
   82         .d_write =      dsp_write,
   83         .d_ioctl =      dsp_ioctl,
   84         .d_poll =       dsp_poll,
   85         .d_mmap =       dsp_mmap,
   86         .d_mmap_single = dsp_mmap_single,
   87         .d_name =       "dsp",
   88 };
   89 
   90 static eventhandler_tag dsp_ehtag = NULL;
   91 static int dsp_umax = -1;
   92 static int dsp_cmax = -1;
   93 
   94 static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group);
   95 static int dsp_oss_syncstart(int sg_id);
   96 static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy);
   97 static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled);
   98 static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
   99 static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
  100 static int dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, int *mask);
  101 #ifdef OSSV4_EXPERIMENT
  102 static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
  103 static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
  104 static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
  105 static int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
  106 static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name);
  107 #endif
  108 
  109 static struct snddev_info *
  110 dsp_get_info(struct cdev *dev)
  111 {
  112         return (devclass_get_softc(pcm_devclass, PCMUNIT(dev)));
  113 }
  114 
  115 static uint32_t
  116 dsp_get_flags(struct cdev *dev)
  117 {
  118         device_t bdev;
  119 
  120         bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev));
  121 
  122         return ((bdev != NULL) ? pcm_getflags(bdev) : 0xffffffff);
  123 }
  124 
  125 static void
  126 dsp_set_flags(struct cdev *dev, uint32_t flags)
  127 {
  128         device_t bdev;
  129 
  130         bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev));
  131 
  132         if (bdev != NULL)
  133                 pcm_setflags(bdev, flags);
  134 }
  135 
  136 /*
  137  * return the channels associated with an open device instance.
  138  * lock channels specified.
  139  */
  140 static int
  141 getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch,
  142     uint32_t prio)
  143 {
  144         struct snddev_info *d;
  145         struct pcm_channel *ch;
  146         uint32_t flags;
  147 
  148         if (PCM_SIMPLEX(dev) != 0) {
  149                 d = dsp_get_info(dev);
  150                 if (!PCM_REGISTERED(d))
  151                         return (ENXIO);
  152                 PCM_LOCK(d);
  153                 PCM_WAIT(d);
  154                 PCM_ACQUIRE(d);
  155                 /*
  156                  * Note: order is important -
  157                  *       pcm flags -> prio query flags -> wild guess
  158                  */
  159                 ch = NULL;
  160                 flags = dsp_get_flags(dev);
  161                 if (flags & SD_F_PRIO_WR) {
  162                         ch = PCM_RDCH(dev);
  163                         PCM_RDCH(dev) = NULL;
  164                 } else if (flags & SD_F_PRIO_RD) {
  165                         ch = PCM_WRCH(dev);
  166                         PCM_WRCH(dev) = NULL;
  167                 } else if (prio & SD_F_PRIO_WR) {
  168                         ch = PCM_RDCH(dev);
  169                         PCM_RDCH(dev) = NULL;
  170                         flags |= SD_F_PRIO_WR;
  171                 } else if (prio & SD_F_PRIO_RD) {
  172                         ch = PCM_WRCH(dev);
  173                         PCM_WRCH(dev) = NULL;
  174                         flags |= SD_F_PRIO_RD;
  175                 } else if (PCM_WRCH(dev) != NULL) {
  176                         ch = PCM_RDCH(dev);
  177                         PCM_RDCH(dev) = NULL;
  178                         flags |= SD_F_PRIO_WR;
  179                 } else if (PCM_RDCH(dev) != NULL) {
  180                         ch = PCM_WRCH(dev);
  181                         PCM_WRCH(dev) = NULL;
  182                         flags |= SD_F_PRIO_RD;
  183                 }
  184                 PCM_SIMPLEX(dev) = 0;
  185                 dsp_set_flags(dev, flags);
  186                 if (ch != NULL) {
  187                         CHN_LOCK(ch);
  188                         pcm_chnref(ch, -1);
  189                         pcm_chnrelease(ch);
  190                 }
  191                 PCM_RELEASE(d);
  192                 PCM_UNLOCK(d);
  193         }
  194 
  195         *rdch = PCM_RDCH(dev);
  196         *wrch = PCM_WRCH(dev);
  197 
  198         if (*rdch != NULL && (prio & SD_F_PRIO_RD))
  199                 CHN_LOCK(*rdch);
  200         if (*wrch != NULL && (prio & SD_F_PRIO_WR))
  201                 CHN_LOCK(*wrch);
  202 
  203         return (0);
  204 }
  205 
  206 /* unlock specified channels */
  207 static void
  208 relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch,
  209     uint32_t prio)
  210 {
  211         if (wrch != NULL && (prio & SD_F_PRIO_WR))
  212                 CHN_UNLOCK(wrch);
  213         if (rdch != NULL && (prio & SD_F_PRIO_RD))
  214                 CHN_UNLOCK(rdch);
  215 }
  216 
  217 static void
  218 dsp_cdevinfo_alloc(struct cdev *dev,
  219     struct pcm_channel *rdch, struct pcm_channel *wrch,
  220     struct pcm_channel *volch)
  221 {
  222         struct snddev_info *d;
  223         struct dsp_cdevinfo *cdi;
  224         int simplex;
  225 
  226         d = dsp_get_info(dev);
  227 
  228         KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL &&
  229             ((rdch == NULL && wrch == NULL) || rdch != wrch),
  230             ("bogus %s(), what are you trying to accomplish here?", __func__));
  231         PCM_BUSYASSERT(d);
  232         PCM_LOCKASSERT(d);
  233 
  234         simplex = (dsp_get_flags(dev) & SD_F_SIMPLEX) ? 1 : 0;
  235 
  236         /*
  237          * Scan for free instance entry and put it into the end of list.
  238          * Create new one if necessary.
  239          */
  240         TAILQ_FOREACH(cdi, &d->dsp_cdevinfo_pool, link) {
  241                 if (cdi->busy != 0)
  242                         break;
  243                 cdi->rdch = rdch;
  244                 cdi->wrch = wrch;
  245                 cdi->volch = volch;
  246                 cdi->simplex = simplex;
  247                 cdi->busy = 1;
  248                 TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
  249                 TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link);
  250                 dev->si_drv1 = cdi;
  251                 return;
  252         }
  253         PCM_UNLOCK(d);
  254         cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO);
  255         PCM_LOCK(d);
  256         cdi->rdch = rdch;
  257         cdi->wrch = wrch;
  258         cdi->volch = volch;
  259         cdi->simplex = simplex;
  260         cdi->busy = 1;
  261         TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link);
  262         dev->si_drv1 = cdi;
  263 }
  264 
  265 static void
  266 dsp_cdevinfo_free(struct cdev *dev)
  267 {
  268         struct snddev_info *d;
  269         struct dsp_cdevinfo *cdi, *tmp;
  270         uint32_t flags;
  271         int i;
  272 
  273         d = dsp_get_info(dev);
  274 
  275         KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL &&
  276             PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL &&
  277             PCM_VOLCH(dev) == NULL,
  278             ("bogus %s(), what are you trying to accomplish here?", __func__));
  279         PCM_BUSYASSERT(d);
  280         PCM_LOCKASSERT(d);
  281 
  282         cdi = dev->si_drv1;
  283         dev->si_drv1 = NULL;
  284         cdi->rdch = NULL;
  285         cdi->wrch = NULL;
  286         cdi->volch = NULL;
  287         cdi->simplex = 0;
  288         cdi->busy = 0;
  289 
  290         /*
  291          * Once it is free, move it back to the beginning of list for
  292          * faster new entry allocation.
  293          */
  294         TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
  295         TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link);
  296 
  297         /*
  298          * Scan the list, cache free entries up to DSP_CDEVINFO_CACHESIZE.
  299          * Reset simplex flags.
  300          */
  301         flags = dsp_get_flags(dev) & ~SD_F_PRIO_SET;
  302         i = DSP_CDEVINFO_CACHESIZE;
  303         TAILQ_FOREACH_SAFE(cdi, &d->dsp_cdevinfo_pool, link, tmp) {
  304                 if (cdi->busy != 0) {
  305                         if (cdi->simplex == 0) {
  306                                 if (cdi->rdch != NULL)
  307                                         flags |= SD_F_PRIO_RD;
  308                                 if (cdi->wrch != NULL)
  309                                         flags |= SD_F_PRIO_WR;
  310                         }
  311                 } else {
  312                         if (i == 0) {
  313                                 TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
  314                                 free(cdi, M_DEVBUF);
  315                         } else
  316                                 i--;
  317                 }
  318         }
  319         dsp_set_flags(dev, flags);
  320 }
  321 
  322 void
  323 dsp_cdevinfo_init(struct snddev_info *d)
  324 {
  325         struct dsp_cdevinfo *cdi;
  326         int i;
  327 
  328         KASSERT(d != NULL, ("NULL snddev_info"));
  329         PCM_BUSYASSERT(d);
  330         PCM_UNLOCKASSERT(d);
  331 
  332         TAILQ_INIT(&d->dsp_cdevinfo_pool);
  333         for (i = 0; i < DSP_CDEVINFO_CACHESIZE; i++) {
  334                 cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO);
  335                 TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link);
  336         }
  337 }
  338 
  339 void
  340 dsp_cdevinfo_flush(struct snddev_info *d)
  341 {
  342         struct dsp_cdevinfo *cdi, *tmp;
  343 
  344         KASSERT(d != NULL, ("NULL snddev_info"));
  345         PCM_BUSYASSERT(d);
  346         PCM_UNLOCKASSERT(d);
  347 
  348         cdi = TAILQ_FIRST(&d->dsp_cdevinfo_pool);
  349         while (cdi != NULL) {
  350                 tmp = TAILQ_NEXT(cdi, link);
  351                 free(cdi, M_DEVBUF);
  352                 cdi = tmp;
  353         }
  354         TAILQ_INIT(&d->dsp_cdevinfo_pool);
  355 }
  356 
  357 /* duplex / simplex cdev type */
  358 enum {
  359         DSP_CDEV_TYPE_RDONLY,           /* simplex read-only (record)   */
  360         DSP_CDEV_TYPE_WRONLY,           /* simplex write-only (play)    */
  361         DSP_CDEV_TYPE_RDWR              /* duplex read, write, or both  */
  362 };
  363 
  364 enum {
  365         DSP_CDEV_VOLCTL_NONE,
  366         DSP_CDEV_VOLCTL_READ,
  367         DSP_CDEV_VOLCTL_WRITE
  368 };
  369 
  370 #define DSP_F_VALID(x)          ((x) & (FREAD | FWRITE))
  371 #define DSP_F_DUPLEX(x)         (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
  372 #define DSP_F_SIMPLEX(x)        (!DSP_F_DUPLEX(x))
  373 #define DSP_F_READ(x)           ((x) & FREAD)
  374 #define DSP_F_WRITE(x)          ((x) & FWRITE)
  375 
  376 static const struct {
  377         int type;
  378         char *name;
  379         char *sep;
  380         char *alias;
  381         int use_sep;
  382         int hw;
  383         int max;
  384         int volctl;
  385         uint32_t fmt, spd;
  386         int query;
  387 } dsp_cdevs[] = {
  388         { SND_DEV_DSP,         "dsp",    ".", NULL, 0, 0, 0, 0,
  389           SND_FORMAT(AFMT_U8, 1, 0),     DSP_DEFAULT_SPEED,
  390           DSP_CDEV_TYPE_RDWR },
  391         { SND_DEV_AUDIO,       "audio",  ".", NULL, 0, 0, 0, 0,
  392           SND_FORMAT(AFMT_MU_LAW, 1, 0), DSP_DEFAULT_SPEED,
  393           DSP_CDEV_TYPE_RDWR },
  394         { SND_DEV_DSP16,       "dspW",   ".", NULL, 0, 0, 0, 0,
  395           SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_DEFAULT_SPEED,
  396           DSP_CDEV_TYPE_RDWR },
  397         { SND_DEV_DSPHW_PLAY,  "dsp",   ".p", NULL, 1, 1, SND_MAXHWCHAN, 1,
  398           SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY },
  399         { SND_DEV_DSPHW_VPLAY, "dsp",  ".vp", NULL, 1, 1, SND_MAXVCHANS, 1,
  400           SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY },
  401         { SND_DEV_DSPHW_REC,   "dsp",   ".r", NULL, 1, 1, SND_MAXHWCHAN, 1,
  402           SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY },
  403         { SND_DEV_DSPHW_VREC,  "dsp",  ".vr", NULL, 1, 1, SND_MAXVCHANS, 1,
  404           SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY },
  405         { SND_DEV_DSPHW_CD,    "dspcd",  ".", NULL, 0, 0, 0, 0,
  406           SND_FORMAT(AFMT_S16_LE, 2, 0), 44100, DSP_CDEV_TYPE_RDWR   },
  407         /* Low priority, OSSv4 aliases. */
  408         { SND_DEV_DSP,      "dsp_ac3",   ".", "dsp", 0, 0, 0, 0,
  409           SND_FORMAT(AFMT_U8, 1, 0),     DSP_DEFAULT_SPEED,
  410           DSP_CDEV_TYPE_RDWR },
  411         { SND_DEV_DSP,     "dsp_mmap",   ".", "dsp", 0, 0, 0, 0,
  412           SND_FORMAT(AFMT_U8, 1, 0),     DSP_DEFAULT_SPEED,
  413           DSP_CDEV_TYPE_RDWR },
  414         { SND_DEV_DSP,  "dsp_multich",   ".", "dsp", 0, 0, 0, 0,
  415           SND_FORMAT(AFMT_U8, 1, 0),     DSP_DEFAULT_SPEED,
  416           DSP_CDEV_TYPE_RDWR },
  417         { SND_DEV_DSP, "dsp_spdifout",   ".", "dsp", 0, 0, 0, 0,
  418           SND_FORMAT(AFMT_U8, 1, 0),     DSP_DEFAULT_SPEED,
  419           DSP_CDEV_TYPE_RDWR },
  420         { SND_DEV_DSP,  "dsp_spdifin",   ".", "dsp", 0, 0, 0, 0,
  421           SND_FORMAT(AFMT_U8, 1, 0),     DSP_DEFAULT_SPEED,
  422           DSP_CDEV_TYPE_RDWR },
  423 };
  424 
  425 #define DSP_FIXUP_ERROR()               do {                            \
  426         prio = dsp_get_flags(i_dev);                                    \
  427         if (!DSP_F_VALID(flags))                                        \
  428                 error = EINVAL;                                         \
  429         if (!DSP_F_DUPLEX(flags) &&                                     \
  430             ((DSP_F_READ(flags) && d->reccount == 0) ||                 \
  431             (DSP_F_WRITE(flags) && d->playcount == 0)))                 \
  432                 error = ENOTSUP;                                        \
  433         else if (!DSP_F_DUPLEX(flags) && (prio & SD_F_SIMPLEX) &&       \
  434             ((DSP_F_READ(flags) && (prio & SD_F_PRIO_WR)) ||            \
  435             (DSP_F_WRITE(flags) && (prio & SD_F_PRIO_RD))))             \
  436                 error = EBUSY;                                          \
  437         else if (DSP_REGISTERED(d, i_dev))                              \
  438                 error = EBUSY;                                          \
  439 } while (0)
  440 
  441 static int
  442 dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
  443 {
  444         struct pcm_channel *rdch, *wrch;
  445         struct snddev_info *d;
  446         uint32_t fmt, spd, prio, volctl;
  447         int i, error, rderror, wrerror, devtype, wdevunit, rdevunit;
  448 
  449         /* Kind of impossible.. */
  450         if (i_dev == NULL || td == NULL)
  451                 return (ENODEV);
  452 
  453         d = dsp_get_info(i_dev);
  454         if (!PCM_REGISTERED(d))
  455                 return (EBADF);
  456 
  457         PCM_GIANT_ENTER(d);
  458 
  459         /* Lock snddev so nobody else can monkey with it. */
  460         PCM_LOCK(d);
  461         PCM_WAIT(d);
  462 
  463         /*
  464          * Try to acquire cloned device before someone else pick it.
  465          * ENODEV means this is not a cloned droids.
  466          */
  467         error = snd_clone_acquire(i_dev);
  468         if (!(error == 0 || error == ENODEV)) {
  469                 DSP_FIXUP_ERROR();
  470                 PCM_UNLOCK(d);
  471                 PCM_GIANT_EXIT(d);
  472                 return (error);
  473         }
  474 
  475         error = 0;
  476         DSP_FIXUP_ERROR();
  477 
  478         if (error != 0) {
  479                 (void)snd_clone_release(i_dev);
  480                 PCM_UNLOCK(d);
  481                 PCM_GIANT_EXIT(d);
  482                 return (error);
  483         }
  484 
  485         /*
  486          * That is just enough. Acquire and unlock pcm lock so
  487          * the other will just have to wait until we finish doing
  488          * everything.
  489          */
  490         PCM_ACQUIRE(d);
  491         PCM_UNLOCK(d);
  492 
  493         devtype = PCMDEV(i_dev);
  494         wdevunit = -1;
  495         rdevunit = -1;
  496         fmt = 0;
  497         spd = 0;
  498         volctl = DSP_CDEV_VOLCTL_NONE;
  499 
  500         for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) {
  501                 if (devtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL)
  502                         continue;
  503                 /*
  504                  * Volume control only valid for DSPHW devices,
  505                  * and it must be opened in opposite direction be it
  506                  * simplex or duplex. Anything else will be handled
  507                  * as usual.
  508                  */
  509                 if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) {
  510                         if (dsp_cdevs[i].volctl != 0 &&
  511                             DSP_F_READ(flags)) {
  512                                 volctl = DSP_CDEV_VOLCTL_WRITE;
  513                                 flags &= ~FREAD;
  514                                 flags |= FWRITE;
  515                         }
  516                         if (DSP_F_READ(flags)) {
  517                                 (void)snd_clone_release(i_dev);
  518                                 PCM_RELEASE_QUICK(d);
  519                                 PCM_GIANT_EXIT(d);
  520                                 return (ENOTSUP);
  521                         }
  522                         wdevunit = dev2unit(i_dev);
  523                 } else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) {
  524                         if (dsp_cdevs[i].volctl != 0 &&
  525                             DSP_F_WRITE(flags)) {
  526                                 volctl = DSP_CDEV_VOLCTL_READ;
  527                                 flags &= ~FWRITE;
  528                                 flags |= FREAD;
  529                         }
  530                         if (DSP_F_WRITE(flags)) {
  531                                 (void)snd_clone_release(i_dev);
  532                                 PCM_RELEASE_QUICK(d);
  533                                 PCM_GIANT_EXIT(d);
  534                                 return (ENOTSUP);
  535                         }
  536                         rdevunit = dev2unit(i_dev);
  537                 }
  538                 fmt = dsp_cdevs[i].fmt;
  539                 spd = dsp_cdevs[i].spd;
  540                 break;
  541         }
  542 
  543         /* No matching devtype? */
  544         if (fmt == 0 || spd == 0)
  545                 panic("impossible devtype %d", devtype);
  546 
  547         rdch = NULL;
  548         wrch = NULL;
  549         rderror = 0;
  550         wrerror = 0;
  551 
  552         /*
  553          * if we get here, the open request is valid- either:
  554          *   * we were previously not open
  555          *   * we were open for play xor record and the opener wants
  556          *     the non-open direction
  557          */
  558         if (DSP_F_READ(flags)) {
  559                 /* open for read */
  560                 rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC,
  561                     td->td_proc->p_pid, td->td_proc->p_comm, rdevunit);
  562 
  563                 if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0)
  564                         rderror = ENXIO;
  565 
  566                 if (volctl == DSP_CDEV_VOLCTL_READ)
  567                         rderror = 0;
  568 
  569                 if (rderror != 0) {
  570                         if (rdch != NULL)
  571                                 pcm_chnrelease(rdch);
  572                         if (!DSP_F_DUPLEX(flags)) {
  573                                 (void)snd_clone_release(i_dev);
  574                                 PCM_RELEASE_QUICK(d);
  575                                 PCM_GIANT_EXIT(d);
  576                                 return (rderror);
  577                         }
  578                         rdch = NULL;
  579                 } else if (volctl == DSP_CDEV_VOLCTL_READ) {
  580                         if (rdch != NULL) {
  581                                 pcm_chnref(rdch, 1);
  582                                 pcm_chnrelease(rdch);
  583                         }
  584                 } else {
  585                         if (flags & O_NONBLOCK)
  586                                 rdch->flags |= CHN_F_NBIO;
  587                         if (flags & O_EXCL)
  588                                 rdch->flags |= CHN_F_EXCLUSIVE;
  589                         pcm_chnref(rdch, 1);
  590                         if (volctl == DSP_CDEV_VOLCTL_NONE)
  591                                 chn_vpc_reset(rdch, SND_VOL_C_PCM, 0);
  592                         CHN_UNLOCK(rdch);
  593                 }
  594         }
  595 
  596         if (DSP_F_WRITE(flags)) {
  597                 /* open for write */
  598                 wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY,
  599                     td->td_proc->p_pid, td->td_proc->p_comm, wdevunit);
  600 
  601                 if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0)
  602                         wrerror = ENXIO;
  603 
  604                 if (volctl == DSP_CDEV_VOLCTL_WRITE)
  605                         wrerror = 0;
  606 
  607                 if (wrerror != 0) {
  608                         if (wrch != NULL)
  609                                 pcm_chnrelease(wrch);
  610                         if (!DSP_F_DUPLEX(flags)) {
  611                                 if (rdch != NULL) {
  612                                         /*
  613                                          * Lock, deref and release previously
  614                                          * created record channel
  615                                          */
  616                                         CHN_LOCK(rdch);
  617                                         pcm_chnref(rdch, -1);
  618                                         pcm_chnrelease(rdch);
  619                                 }
  620                                 (void)snd_clone_release(i_dev);
  621                                 PCM_RELEASE_QUICK(d);
  622                                 PCM_GIANT_EXIT(d);
  623                                 return (wrerror);
  624                         }
  625                         wrch = NULL;
  626                 } else if (volctl == DSP_CDEV_VOLCTL_WRITE) {
  627                         if (wrch != NULL) {
  628                                 pcm_chnref(wrch, 1);
  629                                 pcm_chnrelease(wrch);
  630                         }
  631                 } else {
  632                         if (flags & O_NONBLOCK)
  633                                 wrch->flags |= CHN_F_NBIO;
  634                         if (flags & O_EXCL)
  635                                 wrch->flags |= CHN_F_EXCLUSIVE;
  636                         pcm_chnref(wrch, 1);
  637                         if (volctl == DSP_CDEV_VOLCTL_NONE)
  638                                 chn_vpc_reset(wrch, SND_VOL_C_PCM, 0);
  639                         CHN_UNLOCK(wrch);
  640                 }
  641         }
  642 
  643 
  644         PCM_LOCK(d);
  645 
  646         /*
  647          * We're done. Allocate channels information for this cdev.
  648          */
  649         switch (volctl) {
  650         case DSP_CDEV_VOLCTL_READ:
  651                 KASSERT(wrch == NULL, ("wrch=%p not null!", wrch));
  652                 dsp_cdevinfo_alloc(i_dev, NULL, NULL, rdch);
  653                 break;
  654         case DSP_CDEV_VOLCTL_WRITE:
  655                 KASSERT(rdch == NULL, ("rdch=%p not null!", rdch));
  656                 dsp_cdevinfo_alloc(i_dev, NULL, NULL, wrch);
  657                 break;
  658         case DSP_CDEV_VOLCTL_NONE:
  659         default:
  660                 if (wrch == NULL && rdch == NULL) {
  661                         (void)snd_clone_release(i_dev);
  662                         PCM_RELEASE(d);
  663                         PCM_UNLOCK(d);
  664                         PCM_GIANT_EXIT(d);
  665                         if (wrerror != 0)
  666                                 return (wrerror);
  667                         if (rderror != 0)
  668                                 return (rderror);
  669                         return (EINVAL);
  670                 }
  671                 dsp_cdevinfo_alloc(i_dev, rdch, wrch, NULL);
  672                 if (rdch != NULL)
  673                         CHN_INSERT_HEAD(d, rdch, channels.pcm.opened);
  674                 if (wrch != NULL)
  675                         CHN_INSERT_HEAD(d, wrch, channels.pcm.opened);
  676                 break;
  677         }
  678 
  679         /*
  680          * Increase clone refcount for its automatic garbage collector.
  681          */
  682         (void)snd_clone_ref(i_dev);
  683 
  684         PCM_RELEASE(d);
  685         PCM_UNLOCK(d);
  686 
  687         PCM_GIANT_LEAVE(d);
  688 
  689         return (0);
  690 }
  691 
  692 static int
  693 dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
  694 {
  695         struct pcm_channel *rdch, *wrch, *volch;
  696         struct snddev_info *d;
  697         int sg_ids, rdref, wdref;
  698 
  699         d = dsp_get_info(i_dev);
  700         if (!DSP_REGISTERED(d, i_dev))
  701                 return (EBADF);
  702 
  703         PCM_GIANT_ENTER(d);
  704 
  705         PCM_LOCK(d);
  706         PCM_WAIT(d);
  707         PCM_ACQUIRE(d);
  708 
  709         rdch = PCM_RDCH(i_dev);
  710         wrch = PCM_WRCH(i_dev);
  711         volch = PCM_VOLCH(i_dev);
  712 
  713         PCM_RDCH(i_dev) = NULL;
  714         PCM_WRCH(i_dev) = NULL;
  715         PCM_VOLCH(i_dev) = NULL;
  716 
  717         rdref = -1;
  718         wdref = -1;
  719 
  720         if (volch != NULL) {
  721                 if (volch == rdch)
  722                         rdref--;
  723                 else if (volch == wrch)
  724                         wdref--;
  725                 else {
  726                         CHN_LOCK(volch);
  727                         pcm_chnref(volch, -1);
  728                         CHN_UNLOCK(volch);
  729                 }
  730         }
  731 
  732         if (rdch != NULL)
  733                 CHN_REMOVE(d, rdch, channels.pcm.opened);
  734         if (wrch != NULL)
  735                 CHN_REMOVE(d, wrch, channels.pcm.opened);
  736 
  737         if (rdch != NULL || wrch != NULL) {
  738                 PCM_UNLOCK(d);
  739                 if (rdch != NULL) {
  740                         /*
  741                          * The channel itself need not be locked because:
  742                          *   a)  Adding a channel to a syncgroup happens only
  743                          *       in dsp_ioctl(), which cannot run concurrently
  744                          *       to dsp_close().
  745                          *   b)  The syncmember pointer (sm) is protected by
  746                          *       the global syncgroup list lock.
  747                          *   c)  A channel can't just disappear, invalidating
  748                          *       pointers, unless it's closed/dereferenced
  749                          *       first.
  750                          */
  751                         PCM_SG_LOCK();
  752                         sg_ids = chn_syncdestroy(rdch);
  753                         PCM_SG_UNLOCK();
  754                         if (sg_ids != 0)
  755                                 free_unr(pcmsg_unrhdr, sg_ids);
  756 
  757                         CHN_LOCK(rdch);
  758                         pcm_chnref(rdch, rdref);
  759                         chn_abort(rdch); /* won't sleep */
  760                         rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
  761                             CHN_F_DEAD | CHN_F_EXCLUSIVE);
  762                         chn_reset(rdch, 0, 0);
  763                         pcm_chnrelease(rdch);
  764                 }
  765                 if (wrch != NULL) {
  766                         /*
  767                          * Please see block above.
  768                          */
  769                         PCM_SG_LOCK();
  770                         sg_ids = chn_syncdestroy(wrch);
  771                         PCM_SG_UNLOCK();
  772                         if (sg_ids != 0)
  773                                 free_unr(pcmsg_unrhdr, sg_ids);
  774 
  775                         CHN_LOCK(wrch);
  776                         pcm_chnref(wrch, wdref);
  777                         chn_flush(wrch); /* may sleep */
  778                         wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
  779                             CHN_F_DEAD | CHN_F_EXCLUSIVE);
  780                         chn_reset(wrch, 0, 0);
  781                         pcm_chnrelease(wrch);
  782                 }
  783                 PCM_LOCK(d);
  784         }
  785 
  786         dsp_cdevinfo_free(i_dev);
  787         /*
  788          * Release clone busy state and unref it so the automatic
  789          * garbage collector will get the hint and do the remaining
  790          * cleanup process.
  791          */
  792         (void)snd_clone_release(i_dev);
  793 
  794         /*
  795          * destroy_dev() might sleep, so release pcm lock
  796          * here and rely on pcm cv serialization.
  797          */
  798         PCM_UNLOCK(d);
  799         (void)snd_clone_unref(i_dev);
  800         PCM_LOCK(d);
  801 
  802         PCM_RELEASE(d);
  803         PCM_UNLOCK(d);
  804 
  805         PCM_GIANT_LEAVE(d);
  806 
  807         return (0);
  808 }
  809 
  810 static __inline int
  811 dsp_io_ops(struct cdev *i_dev, struct uio *buf)
  812 {
  813         struct snddev_info *d;
  814         struct pcm_channel **ch, *rdch, *wrch;
  815         int (*chn_io)(struct pcm_channel *, struct uio *);
  816         int prio, ret;
  817         pid_t runpid;
  818 
  819         KASSERT(i_dev != NULL && buf != NULL &&
  820             (buf->uio_rw == UIO_READ || buf->uio_rw == UIO_WRITE),
  821             ("%s(): io train wreck!", __func__));
  822 
  823         d = dsp_get_info(i_dev);
  824         if (!DSP_REGISTERED(d, i_dev))
  825                 return (EBADF);
  826 
  827         PCM_GIANT_ENTER(d);
  828 
  829         switch (buf->uio_rw) {
  830         case UIO_READ:
  831                 prio = SD_F_PRIO_RD;
  832                 ch = &rdch;
  833                 chn_io = chn_read;
  834                 break;
  835         case UIO_WRITE:
  836                 prio = SD_F_PRIO_WR;
  837                 ch = &wrch;
  838                 chn_io = chn_write;
  839                 break;
  840         default:
  841                 panic("invalid/corrupted uio direction: %d", buf->uio_rw);
  842                 break;
  843         }
  844 
  845         rdch = NULL;
  846         wrch = NULL;
  847         runpid = buf->uio_td->td_proc->p_pid;
  848 
  849         getchns(i_dev, &rdch, &wrch, prio);
  850 
  851         if (*ch == NULL || !((*ch)->flags & CHN_F_BUSY)) {
  852                 PCM_GIANT_EXIT(d);
  853                 return (EBADF);
  854         }
  855 
  856         if (((*ch)->flags & (CHN_F_MMAP | CHN_F_DEAD)) ||
  857             (((*ch)->flags & CHN_F_RUNNING) && (*ch)->pid != runpid)) {
  858                 relchns(i_dev, rdch, wrch, prio);
  859                 PCM_GIANT_EXIT(d);
  860                 return (EINVAL);
  861         } else if (!((*ch)->flags & CHN_F_RUNNING)) {
  862                 (*ch)->flags |= CHN_F_RUNNING;
  863                 (*ch)->pid = runpid;
  864         }
  865 
  866         /*
  867          * chn_read/write must give up channel lock in order to copy bytes
  868          * from/to userland, so up the "in progress" counter to make sure
  869          * someone else doesn't come along and muss up the buffer.
  870          */
  871         ++(*ch)->inprog;
  872         ret = chn_io(*ch, buf);
  873         --(*ch)->inprog;
  874 
  875         CHN_BROADCAST(&(*ch)->cv);
  876 
  877         relchns(i_dev, rdch, wrch, prio);
  878 
  879         PCM_GIANT_LEAVE(d);
  880 
  881         return (ret);
  882 }
  883 
  884 static int
  885 dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
  886 {
  887         return (dsp_io_ops(i_dev, buf));
  888 }
  889 
  890 static int
  891 dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
  892 {
  893         return (dsp_io_ops(i_dev, buf));
  894 }
  895 
  896 static int
  897 dsp_get_volume_channel(struct cdev *dev, struct pcm_channel **volch)
  898 {
  899         struct snddev_info *d;
  900         struct pcm_channel *c;
  901         int unit;
  902 
  903         KASSERT(dev != NULL && volch != NULL,
  904             ("%s(): NULL query dev=%p volch=%p", __func__, dev, volch));
  905 
  906         d = dsp_get_info(dev);
  907         if (!PCM_REGISTERED(d)) {
  908                 *volch = NULL;
  909                 return (EINVAL);
  910         }
  911 
  912         PCM_UNLOCKASSERT(d);
  913 
  914         *volch = NULL;
  915 
  916         c = PCM_VOLCH(dev);
  917         if (c != NULL) {
  918                 if (!(c->feederflags & (1 << FEEDER_VOLUME)))
  919                         return (-1);
  920                 *volch = c;
  921                 return (0);
  922         }
  923 
  924         PCM_LOCK(d);
  925         PCM_WAIT(d);
  926         PCM_ACQUIRE(d);
  927 
  928         unit = dev2unit(dev);
  929 
  930         CHN_FOREACH(c, d, channels.pcm) {
  931                 CHN_LOCK(c);
  932                 if (c->unit != unit) {
  933                         CHN_UNLOCK(c);
  934                         continue;
  935                 }
  936                 *volch = c;
  937                 pcm_chnref(c, 1);
  938                 PCM_VOLCH(dev) = c;
  939                 CHN_UNLOCK(c);
  940                 PCM_RELEASE(d);
  941                 PCM_UNLOCK(d);
  942                 return ((c->feederflags & (1 << FEEDER_VOLUME)) ? 0 : -1);
  943         }
  944 
  945         PCM_RELEASE(d);
  946         PCM_UNLOCK(d);
  947 
  948         return (EINVAL);
  949 }
  950 
  951 static int
  952 dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd,
  953     caddr_t arg)
  954 {
  955         struct snddev_info *d;
  956         struct pcm_channel *rdch, *wrch;
  957         int j, devtype, ret;
  958 
  959         d = dsp_get_info(dev);
  960         if (!PCM_REGISTERED(d) || !(dsp_get_flags(dev) & SD_F_VPC))
  961                 return (-1);
  962 
  963         PCM_UNLOCKASSERT(d);
  964 
  965         j = cmd & 0xff;
  966 
  967         rdch = PCM_RDCH(dev);
  968         wrch = PCM_WRCH(dev);
  969 
  970         /* No specific channel, look into cache */
  971         if (volch == NULL)
  972                 volch = PCM_VOLCH(dev);
  973 
  974         /* Look harder */
  975         if (volch == NULL) {
  976                 if (j == SOUND_MIXER_RECLEV && rdch != NULL)
  977                         volch = rdch;
  978                 else if (j == SOUND_MIXER_PCM && wrch != NULL)
  979                         volch = wrch;
  980         }
  981 
  982         devtype = PCMDEV(dev);
  983 
  984         /* Look super harder */
  985         if (volch == NULL &&
  986             (devtype == SND_DEV_DSPHW_PLAY || devtype == SND_DEV_DSPHW_VPLAY ||
  987             devtype == SND_DEV_DSPHW_REC || devtype == SND_DEV_DSPHW_VREC)) {
  988                 ret = dsp_get_volume_channel(dev, &volch);
  989                 if (ret != 0)
  990                         return (ret);
  991                 if (volch == NULL)
  992                         return (EINVAL);
  993         }
  994 
  995         /* Final validation */
  996         if (volch != NULL) {
  997                 CHN_LOCK(volch);
  998                 if (!(volch->feederflags & (1 << FEEDER_VOLUME))) {
  999                         CHN_UNLOCK(volch);
 1000                         return (-1);
 1001                 }
 1002                 if (volch->direction == PCMDIR_PLAY)
 1003                         wrch = volch;
 1004                 else
 1005                         rdch = volch;
 1006         }
 1007 
 1008         ret = EINVAL;
 1009 
 1010         if (volch != NULL &&
 1011             ((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) ||
 1012             (j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) {
 1013                 if ((cmd & ~0xff) == MIXER_WRITE(0)) {
 1014                         int left, right, center;
 1015 
 1016                         left = *(int *)arg & 0x7f;
 1017                         right = ((*(int *)arg) >> 8) & 0x7f;
 1018                         center = (left + right) >> 1;
 1019                         chn_setvolume_multi(volch, SND_VOL_C_PCM, left, right,
 1020                             center);
 1021                 } else if ((cmd & ~0xff) == MIXER_READ(0)) {
 1022                         *(int *)arg = CHN_GETVOLUME(volch,
 1023                                 SND_VOL_C_PCM, SND_CHN_T_FL);
 1024                         *(int *)arg |= CHN_GETVOLUME(volch,
 1025                                 SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
 1026                 }
 1027                 ret = 0;
 1028         } else if (rdch != NULL || wrch != NULL) {
 1029                 switch (j) {
 1030                 case SOUND_MIXER_DEVMASK:
 1031                 case SOUND_MIXER_CAPS:
 1032                 case SOUND_MIXER_STEREODEVS:
 1033                         if ((cmd & ~0xff) == MIXER_READ(0)) {
 1034                                 *(int *)arg = 0;
 1035                                 if (rdch != NULL)
 1036                                         *(int *)arg |= SOUND_MASK_RECLEV;
 1037                                 if (wrch != NULL)
 1038                                         *(int *)arg |= SOUND_MASK_PCM;
 1039                         }
 1040                         ret = 0;
 1041                         break;
 1042                 case SOUND_MIXER_RECMASK:
 1043                 case SOUND_MIXER_RECSRC:
 1044                         if ((cmd & ~0xff) == MIXER_READ(0))
 1045                                 *(int *)arg = 0;
 1046                         ret = 0;
 1047                         break;
 1048                 default:
 1049                         break;
 1050                 }
 1051         }
 1052 
 1053         if (volch != NULL)
 1054                 CHN_UNLOCK(volch);
 1055 
 1056         return (ret);
 1057 }
 1058 
 1059 static int
 1060 dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
 1061     struct thread *td)
 1062 {
 1063         struct pcm_channel *chn, *rdch, *wrch;
 1064         struct snddev_info *d;
 1065         u_long xcmd;
 1066         int *arg_i, ret, tmp;
 1067 
 1068         d = dsp_get_info(i_dev);
 1069         if (!DSP_REGISTERED(d, i_dev))
 1070                 return (EBADF);
 1071 
 1072         PCM_GIANT_ENTER(d);
 1073 
 1074         arg_i = (int *)arg;
 1075         ret = 0;
 1076         xcmd = 0;
 1077         chn = NULL;
 1078 
 1079         if (IOCGROUP(cmd) == 'M') {
 1080                 if (cmd == OSS_GETVERSION) {
 1081                         *arg_i = SOUND_VERSION;
 1082                         PCM_GIANT_EXIT(d);
 1083                         return (0);
 1084                 }
 1085                 ret = dsp_ioctl_channel(i_dev, PCM_VOLCH(i_dev), cmd, arg);
 1086                 if (ret != -1) {
 1087                         PCM_GIANT_EXIT(d);
 1088                         return (ret);
 1089                 }
 1090 
 1091                 if (d->mixer_dev != NULL) {
 1092                         PCM_ACQUIRE_QUICK(d);
 1093                         ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td,
 1094                             MIXER_CMD_DIRECT);
 1095                         PCM_RELEASE_QUICK(d);
 1096                 } else
 1097                         ret = EBADF;
 1098 
 1099                 PCM_GIANT_EXIT(d);
 1100 
 1101                 return (ret);
 1102         }
 1103 
 1104         /*
 1105          * Certain ioctls may be made on any type of device (audio, mixer,
 1106          * and MIDI).  Handle those special cases here.
 1107          */
 1108         if (IOCGROUP(cmd) == 'X') {
 1109                 PCM_ACQUIRE_QUICK(d);
 1110                 switch(cmd) {
 1111                 case SNDCTL_SYSINFO:
 1112                         sound_oss_sysinfo((oss_sysinfo *)arg);
 1113                         break;
 1114                 case SNDCTL_CARDINFO:
 1115                         ret = sound_oss_card_info((oss_card_info *)arg);
 1116                         break;
 1117                 case SNDCTL_AUDIOINFO:
 1118                 case SNDCTL_AUDIOINFO_EX:
 1119                 case SNDCTL_ENGINEINFO:
 1120                         ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg);
 1121                         break;
 1122                 case SNDCTL_MIXERINFO:
 1123                         ret = mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg);
 1124                         break;
 1125                 default:
 1126                         ret = EINVAL;
 1127                 }
 1128                 PCM_RELEASE_QUICK(d);
 1129                 PCM_GIANT_EXIT(d);
 1130                 return (ret);
 1131         }
 1132 
 1133         getchns(i_dev, &rdch, &wrch, 0);
 1134 
 1135         if (wrch != NULL && (wrch->flags & CHN_F_DEAD))
 1136                 wrch = NULL;
 1137         if (rdch != NULL && (rdch->flags & CHN_F_DEAD))
 1138                 rdch = NULL;
 1139 
 1140         if (wrch == NULL && rdch == NULL) {
 1141                 PCM_GIANT_EXIT(d);
 1142                 return (EINVAL);
 1143         }
 1144 
 1145         switch(cmd) {
 1146 #ifdef OLDPCM_IOCTL
 1147         /*
 1148          * we start with the new ioctl interface.
 1149          */
 1150         case AIONWRITE: /* how many bytes can write ? */
 1151                 if (wrch) {
 1152                         CHN_LOCK(wrch);
 1153 /*
 1154                 if (wrch && wrch->bufhard.dl)
 1155                         while (chn_wrfeed(wrch) == 0);
 1156 */
 1157                         *arg_i = sndbuf_getfree(wrch->bufsoft);
 1158                         CHN_UNLOCK(wrch);
 1159                 } else {
 1160                         *arg_i = 0;
 1161                         ret = EINVAL;
 1162                 }
 1163                 break;
 1164 
 1165         case AIOSSIZE:     /* set the current blocksize */
 1166                 {
 1167                         struct snd_size *p = (struct snd_size *)arg;
 1168 
 1169                         p->play_size = 0;
 1170                         p->rec_size = 0;
 1171                         PCM_ACQUIRE_QUICK(d);
 1172                         if (wrch) {
 1173                                 CHN_LOCK(wrch);
 1174                                 chn_setblocksize(wrch, 2, p->play_size);
 1175                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
 1176                                 CHN_UNLOCK(wrch);
 1177                         }
 1178                         if (rdch) {
 1179                                 CHN_LOCK(rdch);
 1180                                 chn_setblocksize(rdch, 2, p->rec_size);
 1181                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
 1182                                 CHN_UNLOCK(rdch);
 1183                         }
 1184                         PCM_RELEASE_QUICK(d);
 1185                 }
 1186                 break;
 1187         case AIOGSIZE:  /* get the current blocksize */
 1188                 {
 1189                         struct snd_size *p = (struct snd_size *)arg;
 1190 
 1191                         if (wrch) {
 1192                                 CHN_LOCK(wrch);
 1193                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
 1194                                 CHN_UNLOCK(wrch);
 1195                         }
 1196                         if (rdch) {
 1197                                 CHN_LOCK(rdch);
 1198                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
 1199                                 CHN_UNLOCK(rdch);
 1200                         }
 1201                 }
 1202                 break;
 1203 
 1204         case AIOSFMT:
 1205         case AIOGFMT:
 1206                 {
 1207                         snd_chan_param *p = (snd_chan_param *)arg;
 1208 
 1209                         if (cmd == AIOSFMT &&
 1210                             ((p->play_format != 0 && p->play_rate == 0) ||
 1211                             (p->rec_format != 0 && p->rec_rate == 0))) {
 1212                                 ret = EINVAL;
 1213                                 break;
 1214                         }
 1215                         PCM_ACQUIRE_QUICK(d);
 1216                         if (wrch) {
 1217                                 CHN_LOCK(wrch);
 1218                                 if (cmd == AIOSFMT && p->play_format != 0) {
 1219                                         chn_setformat(wrch,
 1220                                             SND_FORMAT(p->play_format,
 1221                                             AFMT_CHANNEL(wrch->format),
 1222                                             AFMT_EXTCHANNEL(wrch->format)));
 1223                                         chn_setspeed(wrch, p->play_rate);
 1224                                 }
 1225                                 p->play_rate = wrch->speed;
 1226                                 p->play_format = AFMT_ENCODING(wrch->format);
 1227                                 CHN_UNLOCK(wrch);
 1228                         } else {
 1229                                 p->play_rate = 0;
 1230                                 p->play_format = 0;
 1231                         }
 1232                         if (rdch) {
 1233                                 CHN_LOCK(rdch);
 1234                                 if (cmd == AIOSFMT && p->rec_format != 0) {
 1235                                         chn_setformat(rdch,
 1236                                             SND_FORMAT(p->rec_format,
 1237                                             AFMT_CHANNEL(rdch->format),
 1238                                             AFMT_EXTCHANNEL(rdch->format)));
 1239                                         chn_setspeed(rdch, p->rec_rate);
 1240                                 }
 1241                                 p->rec_rate = rdch->speed;
 1242                                 p->rec_format = AFMT_ENCODING(rdch->format);
 1243                                 CHN_UNLOCK(rdch);
 1244                         } else {
 1245                                 p->rec_rate = 0;
 1246                                 p->rec_format = 0;
 1247                         }
 1248                         PCM_RELEASE_QUICK(d);
 1249                 }
 1250                 break;
 1251 
 1252         case AIOGCAP:     /* get capabilities */
 1253                 {
 1254                         snd_capabilities *p = (snd_capabilities *)arg;
 1255                         struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
 1256                         struct cdev *pdev;
 1257 
 1258                         PCM_LOCK(d);
 1259                         if (rdch) {
 1260                                 CHN_LOCK(rdch);
 1261                                 rcaps = chn_getcaps(rdch);
 1262                         }
 1263                         if (wrch) {
 1264                                 CHN_LOCK(wrch);
 1265                                 pcaps = chn_getcaps(wrch);
 1266                         }
 1267                         p->rate_min = max(rcaps? rcaps->minspeed : 0,
 1268                                           pcaps? pcaps->minspeed : 0);
 1269                         p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
 1270                                           pcaps? pcaps->maxspeed : 1000000);
 1271                         p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
 1272                                          wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
 1273                         /* XXX bad on sb16 */
 1274                         p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
 1275                                      (wrch? chn_getformats(wrch) : 0xffffffff);
 1276                         if (rdch && wrch)
 1277                                 p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
 1278                         pdev = d->mixer_dev;
 1279                         p->mixers = 1; /* default: one mixer */
 1280                         p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
 1281                         p->left = p->right = 100;
 1282                         if (wrch)
 1283                                 CHN_UNLOCK(wrch);
 1284                         if (rdch)
 1285                                 CHN_UNLOCK(rdch);
 1286                         PCM_UNLOCK(d);
 1287                 }
 1288                 break;
 1289 
 1290         case AIOSTOP:
 1291                 if (*arg_i == AIOSYNC_PLAY && wrch) {
 1292                         CHN_LOCK(wrch);
 1293                         *arg_i = chn_abort(wrch);
 1294                         CHN_UNLOCK(wrch);
 1295                 } else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
 1296                         CHN_LOCK(rdch);
 1297                         *arg_i = chn_abort(rdch);
 1298                         CHN_UNLOCK(rdch);
 1299                 } else {
 1300                         printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
 1301                         *arg_i = 0;
 1302                 }
 1303                 break;
 1304 
 1305         case AIOSYNC:
 1306                 printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
 1307                         ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
 1308                 break;
 1309 #endif
 1310         /*
 1311          * here follow the standard ioctls (filio.h etc.)
 1312          */
 1313         case FIONREAD: /* get # bytes to read */
 1314                 if (rdch) {
 1315                         CHN_LOCK(rdch);
 1316 /*                      if (rdch && rdch->bufhard.dl)
 1317                                 while (chn_rdfeed(rdch) == 0);
 1318 */
 1319                         *arg_i = sndbuf_getready(rdch->bufsoft);
 1320                         CHN_UNLOCK(rdch);
 1321                 } else {
 1322                         *arg_i = 0;
 1323                         ret = EINVAL;
 1324                 }
 1325                 break;
 1326 
 1327         case FIOASYNC: /*set/clear async i/o */
 1328                 DEB( printf("FIOASYNC\n") ; )
 1329                 break;
 1330 
 1331         case SNDCTL_DSP_NONBLOCK: /* set non-blocking i/o */
 1332         case FIONBIO: /* set/clear non-blocking i/o */
 1333                 if (rdch) {
 1334                         CHN_LOCK(rdch);
 1335                         if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i)
 1336                                 rdch->flags |= CHN_F_NBIO;
 1337                         else
 1338                                 rdch->flags &= ~CHN_F_NBIO;
 1339                         CHN_UNLOCK(rdch);
 1340                 }
 1341                 if (wrch) {
 1342                         CHN_LOCK(wrch);
 1343                         if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i)
 1344                                 wrch->flags |= CHN_F_NBIO;
 1345                         else
 1346                                 wrch->flags &= ~CHN_F_NBIO;
 1347                         CHN_UNLOCK(wrch);
 1348                 }
 1349                 break;
 1350 
 1351         /*
 1352          * Finally, here is the linux-compatible ioctl interface
 1353          */
 1354 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
 1355         case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
 1356         case SNDCTL_DSP_GETBLKSIZE:
 1357                 chn = wrch ? wrch : rdch;
 1358                 if (chn) {
 1359                         CHN_LOCK(chn);
 1360                         *arg_i = sndbuf_getblksz(chn->bufsoft);
 1361                         CHN_UNLOCK(chn);
 1362                 } else {
 1363                         *arg_i = 0;
 1364                         ret = EINVAL;
 1365                 }
 1366                 break;
 1367 
 1368         case SNDCTL_DSP_SETBLKSIZE:
 1369                 RANGE(*arg_i, 16, 65536);
 1370                 PCM_ACQUIRE_QUICK(d);
 1371                 if (wrch) {
 1372                         CHN_LOCK(wrch);
 1373                         chn_setblocksize(wrch, 2, *arg_i);
 1374                         CHN_UNLOCK(wrch);
 1375                 }
 1376                 if (rdch) {
 1377                         CHN_LOCK(rdch);
 1378                         chn_setblocksize(rdch, 2, *arg_i);
 1379                         CHN_UNLOCK(rdch);
 1380                 }
 1381                 PCM_RELEASE_QUICK(d);
 1382                 break;
 1383 
 1384         case SNDCTL_DSP_RESET:
 1385                 DEB(printf("dsp reset\n"));
 1386                 if (wrch) {
 1387                         CHN_LOCK(wrch);
 1388                         chn_abort(wrch);
 1389                         chn_resetbuf(wrch);
 1390                         CHN_UNLOCK(wrch);
 1391                 }
 1392                 if (rdch) {
 1393                         CHN_LOCK(rdch);
 1394                         chn_abort(rdch);
 1395                         chn_resetbuf(rdch);
 1396                         CHN_UNLOCK(rdch);
 1397                 }
 1398                 break;
 1399 
 1400         case SNDCTL_DSP_SYNC:
 1401                 DEB(printf("dsp sync\n"));
 1402                 /* chn_sync may sleep */
 1403                 if (wrch) {
 1404                         CHN_LOCK(wrch);
 1405                         chn_sync(wrch, 0);
 1406                         CHN_UNLOCK(wrch);
 1407                 }
 1408                 break;
 1409 
 1410         case SNDCTL_DSP_SPEED:
 1411                 /* chn_setspeed may sleep */
 1412                 tmp = 0;
 1413                 PCM_ACQUIRE_QUICK(d);
 1414                 if (wrch) {
 1415                         CHN_LOCK(wrch);
 1416                         ret = chn_setspeed(wrch, *arg_i);
 1417                         tmp = wrch->speed;
 1418                         CHN_UNLOCK(wrch);
 1419                 }
 1420                 if (rdch && ret == 0) {
 1421                         CHN_LOCK(rdch);
 1422                         ret = chn_setspeed(rdch, *arg_i);
 1423                         if (tmp == 0)
 1424                                 tmp = rdch->speed;
 1425                         CHN_UNLOCK(rdch);
 1426                 }
 1427                 PCM_RELEASE_QUICK(d);
 1428                 *arg_i = tmp;
 1429                 break;
 1430 
 1431         case SOUND_PCM_READ_RATE:
 1432                 chn = wrch ? wrch : rdch;
 1433                 if (chn) {
 1434                         CHN_LOCK(chn);
 1435                         *arg_i = chn->speed;
 1436                         CHN_UNLOCK(chn);
 1437                 } else {
 1438                         *arg_i = 0;
 1439                         ret = EINVAL;
 1440                 }
 1441                 break;
 1442 
 1443         case SNDCTL_DSP_STEREO:
 1444                 tmp = -1;
 1445                 *arg_i = (*arg_i)? 2 : 1;
 1446                 PCM_ACQUIRE_QUICK(d);
 1447                 if (wrch) {
 1448                         CHN_LOCK(wrch);
 1449                         ret = chn_setformat(wrch,
 1450                             SND_FORMAT(wrch->format, *arg_i, 0));
 1451                         tmp = (AFMT_CHANNEL(wrch->format) > 1)? 1 : 0;
 1452                         CHN_UNLOCK(wrch);
 1453                 }
 1454                 if (rdch && ret == 0) {
 1455                         CHN_LOCK(rdch);
 1456                         ret = chn_setformat(rdch,
 1457                             SND_FORMAT(rdch->format, *arg_i, 0));
 1458                         if (tmp == -1)
 1459                                 tmp = (AFMT_CHANNEL(rdch->format) > 1)? 1 : 0;
 1460                         CHN_UNLOCK(rdch);
 1461                 }
 1462                 PCM_RELEASE_QUICK(d);
 1463                 *arg_i = tmp;
 1464                 break;
 1465 
 1466         case SOUND_PCM_WRITE_CHANNELS:
 1467 /*      case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
 1468                 if (*arg_i < 0) {
 1469                         *arg_i = 0;
 1470                         ret = EINVAL;
 1471                         break;
 1472                 }
 1473                 if (*arg_i != 0) {
 1474                         struct pcmchan_matrix *m;
 1475                         uint32_t ext;
 1476 
 1477                         tmp = 0;
 1478                         if (*arg_i > SND_CHN_MAX)
 1479                                 *arg_i = SND_CHN_MAX;
 1480 
 1481                         m = feeder_matrix_default_channel_map(*arg_i);
 1482                         if (m != NULL)
 1483                                 ext = m->ext;
 1484                         else
 1485                                 ext = 0;
 1486 
 1487                         PCM_ACQUIRE_QUICK(d);
 1488                         if (wrch) {
 1489                                 CHN_LOCK(wrch);
 1490                                 ret = chn_setformat(wrch,
 1491                                     SND_FORMAT(wrch->format, *arg_i, ext));
 1492                                 tmp = AFMT_CHANNEL(wrch->format);
 1493                                 CHN_UNLOCK(wrch);
 1494                         }
 1495                         if (rdch && ret == 0) {
 1496                                 CHN_LOCK(rdch);
 1497                                 ret = chn_setformat(rdch,
 1498                                     SND_FORMAT(rdch->format, *arg_i, ext));
 1499                                 if (tmp == 0)
 1500                                         tmp = AFMT_CHANNEL(rdch->format);
 1501                                 CHN_UNLOCK(rdch);
 1502                         }
 1503                         PCM_RELEASE_QUICK(d);
 1504                         *arg_i = tmp;
 1505                 } else {
 1506                         chn = wrch ? wrch : rdch;
 1507                         CHN_LOCK(chn);
 1508                         *arg_i = AFMT_CHANNEL(chn->format);
 1509                         CHN_UNLOCK(chn);
 1510                 }
 1511                 break;
 1512 
 1513         case SOUND_PCM_READ_CHANNELS:
 1514                 chn = wrch ? wrch : rdch;
 1515                 if (chn) {
 1516                         CHN_LOCK(chn);
 1517                         *arg_i = AFMT_CHANNEL(chn->format);
 1518                         CHN_UNLOCK(chn);
 1519                 } else {
 1520                         *arg_i = 0;
 1521                         ret = EINVAL;
 1522                 }
 1523                 break;
 1524 
 1525         case SNDCTL_DSP_GETFMTS:        /* returns a mask of supported fmts */
 1526                 chn = wrch ? wrch : rdch;
 1527                 if (chn) {
 1528                         CHN_LOCK(chn);
 1529                         *arg_i = chn_getformats(chn);
 1530                         CHN_UNLOCK(chn);
 1531                 } else {
 1532                         *arg_i = 0;
 1533                         ret = EINVAL;
 1534                 }
 1535                 break;
 1536 
 1537         case SNDCTL_DSP_SETFMT: /* sets _one_ format */
 1538                 if (*arg_i != AFMT_QUERY) {
 1539                         tmp = 0;
 1540                         PCM_ACQUIRE_QUICK(d);
 1541                         if (wrch) {
 1542                                 CHN_LOCK(wrch);
 1543                                 ret = chn_setformat(wrch, SND_FORMAT(*arg_i,
 1544                                     AFMT_CHANNEL(wrch->format),
 1545                                     AFMT_EXTCHANNEL(wrch->format)));
 1546                                 tmp = wrch->format;
 1547                                 CHN_UNLOCK(wrch);
 1548                         }
 1549                         if (rdch && ret == 0) {
 1550                                 CHN_LOCK(rdch);
 1551                                 ret = chn_setformat(rdch, SND_FORMAT(*arg_i,
 1552                                     AFMT_CHANNEL(rdch->format),
 1553                                     AFMT_EXTCHANNEL(rdch->format)));
 1554                                 if (tmp == 0)
 1555                                         tmp = rdch->format;
 1556                                 CHN_UNLOCK(rdch);
 1557                         }
 1558                         PCM_RELEASE_QUICK(d);
 1559                         *arg_i = AFMT_ENCODING(tmp);
 1560                 } else {
 1561                         chn = wrch ? wrch : rdch;
 1562                         CHN_LOCK(chn);
 1563                         *arg_i = AFMT_ENCODING(chn->format);
 1564                         CHN_UNLOCK(chn);
 1565                 }
 1566                 break;
 1567 
 1568         case SNDCTL_DSP_SETFRAGMENT:
 1569                 DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
 1570                 {
 1571                         uint32_t fragln = (*arg_i) & 0x0000ffff;
 1572                         uint32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
 1573                         uint32_t fragsz;
 1574                         uint32_t r_maxfrags, r_fragsz;
 1575 
 1576                         RANGE(fragln, 4, 16);
 1577                         fragsz = 1 << fragln;
 1578 
 1579                         if (maxfrags == 0)
 1580                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
 1581                         if (maxfrags < 2)
 1582                                 maxfrags = 2;
 1583                         if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
 1584                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
 1585 
 1586                         DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
 1587                         PCM_ACQUIRE_QUICK(d);
 1588                         if (rdch) {
 1589                                 CHN_LOCK(rdch);
 1590                                 ret = chn_setblocksize(rdch, maxfrags, fragsz);
 1591                                 r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
 1592                                 r_fragsz = sndbuf_getblksz(rdch->bufsoft);
 1593                                 CHN_UNLOCK(rdch);
 1594                         } else {
 1595                                 r_maxfrags = maxfrags;
 1596                                 r_fragsz = fragsz;
 1597                         }
 1598                         if (wrch && ret == 0) {
 1599                                 CHN_LOCK(wrch);
 1600                                 ret = chn_setblocksize(wrch, maxfrags, fragsz);
 1601                                 maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
 1602                                 fragsz = sndbuf_getblksz(wrch->bufsoft);
 1603                                 CHN_UNLOCK(wrch);
 1604                         } else { /* use whatever came from the read channel */
 1605                                 maxfrags = r_maxfrags;
 1606                                 fragsz = r_fragsz;
 1607                         }
 1608                         PCM_RELEASE_QUICK(d);
 1609 
 1610                         fragln = 0;
 1611                         while (fragsz > 1) {
 1612                                 fragln++;
 1613                                 fragsz >>= 1;
 1614                         }
 1615                         *arg_i = (maxfrags << 16) | fragln;
 1616                 }
 1617                 break;
 1618 
 1619         case SNDCTL_DSP_GETISPACE:
 1620                 /* return the size of data available in the input queue */
 1621                 {
 1622                         audio_buf_info *a = (audio_buf_info *)arg;
 1623                         if (rdch) {
 1624                                 struct snd_dbuf *bs = rdch->bufsoft;
 1625 
 1626                                 CHN_LOCK(rdch);
 1627                                 a->bytes = sndbuf_getready(bs);
 1628                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
 1629                                 a->fragstotal = sndbuf_getblkcnt(bs);
 1630                                 a->fragsize = sndbuf_getblksz(bs);
 1631                                 CHN_UNLOCK(rdch);
 1632                         } else
 1633                                 ret = EINVAL;
 1634                 }
 1635                 break;
 1636 
 1637         case SNDCTL_DSP_GETOSPACE:
 1638                 /* return space available in the output queue */
 1639                 {
 1640                         audio_buf_info *a = (audio_buf_info *)arg;
 1641                         if (wrch) {
 1642                                 struct snd_dbuf *bs = wrch->bufsoft;
 1643 
 1644                                 CHN_LOCK(wrch);
 1645                                 /* XXX abusive DMA update: chn_wrupdate(wrch); */
 1646                                 a->bytes = sndbuf_getfree(bs);
 1647                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
 1648                                 a->fragstotal = sndbuf_getblkcnt(bs);
 1649                                 a->fragsize = sndbuf_getblksz(bs);
 1650                                 CHN_UNLOCK(wrch);
 1651                         } else
 1652                                 ret = EINVAL;
 1653                 }
 1654                 break;
 1655 
 1656         case SNDCTL_DSP_GETIPTR:
 1657                 {
 1658                         count_info *a = (count_info *)arg;
 1659                         if (rdch) {
 1660                                 struct snd_dbuf *bs = rdch->bufsoft;
 1661 
 1662                                 CHN_LOCK(rdch);
 1663                                 /* XXX abusive DMA update: chn_rdupdate(rdch); */
 1664                                 a->bytes = sndbuf_gettotal(bs);
 1665                                 a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
 1666                                 a->ptr = sndbuf_getfreeptr(bs);
 1667                                 rdch->blocks = sndbuf_getblocks(bs);
 1668                                 CHN_UNLOCK(rdch);
 1669                         } else
 1670                                 ret = EINVAL;
 1671                 }
 1672                 break;
 1673 
 1674         case SNDCTL_DSP_GETOPTR:
 1675                 {
 1676                         count_info *a = (count_info *)arg;
 1677                         if (wrch) {
 1678                                 struct snd_dbuf *bs = wrch->bufsoft;
 1679 
 1680                                 CHN_LOCK(wrch);
 1681                                 /* XXX abusive DMA update: chn_wrupdate(wrch); */
 1682                                 a->bytes = sndbuf_gettotal(bs);
 1683                                 a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
 1684                                 a->ptr = sndbuf_getreadyptr(bs);
 1685                                 wrch->blocks = sndbuf_getblocks(bs);
 1686                                 CHN_UNLOCK(wrch);
 1687                         } else
 1688                                 ret = EINVAL;
 1689                 }
 1690                 break;
 1691 
 1692         case SNDCTL_DSP_GETCAPS:
 1693                 PCM_LOCK(d);
 1694                 *arg_i = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER;
 1695                 if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
 1696                         *arg_i |= PCM_CAP_DUPLEX;
 1697                 PCM_UNLOCK(d);
 1698                 break;
 1699 
 1700         case SOUND_PCM_READ_BITS:
 1701                 chn = wrch ? wrch : rdch;
 1702                 if (chn) {
 1703                         CHN_LOCK(chn);
 1704                         if (chn->format & AFMT_8BIT)
 1705                                 *arg_i = 8;
 1706                         else if (chn->format & AFMT_16BIT)
 1707                                 *arg_i = 16;
 1708                         else if (chn->format & AFMT_24BIT)
 1709                                 *arg_i = 24;
 1710                         else if (chn->format & AFMT_32BIT)
 1711                                 *arg_i = 32;
 1712                         else
 1713                                 ret = EINVAL;
 1714                         CHN_UNLOCK(chn);
 1715                 } else {
 1716                         *arg_i = 0;
 1717                         ret = EINVAL;
 1718                 }
 1719                 break;
 1720 
 1721         case SNDCTL_DSP_SETTRIGGER:
 1722                 if (rdch) {
 1723                         CHN_LOCK(rdch);
 1724                         rdch->flags &= ~CHN_F_NOTRIGGER;
 1725                         if (*arg_i & PCM_ENABLE_INPUT)
 1726                                 chn_start(rdch, 1);
 1727                         else {
 1728                                 chn_abort(rdch);
 1729                                 chn_resetbuf(rdch);
 1730                                 rdch->flags |= CHN_F_NOTRIGGER;
 1731                         }
 1732                         CHN_UNLOCK(rdch);
 1733                 }
 1734                 if (wrch) {
 1735                         CHN_LOCK(wrch);
 1736                         wrch->flags &= ~CHN_F_NOTRIGGER;
 1737                         if (*arg_i & PCM_ENABLE_OUTPUT)
 1738                                 chn_start(wrch, 1);
 1739                         else {
 1740                                 chn_abort(wrch);
 1741                                 chn_resetbuf(wrch);
 1742                                 wrch->flags |= CHN_F_NOTRIGGER;
 1743                         }
 1744                         CHN_UNLOCK(wrch);
 1745                 }
 1746                 break;
 1747 
 1748         case SNDCTL_DSP_GETTRIGGER:
 1749                 *arg_i = 0;
 1750                 if (wrch) {
 1751                         CHN_LOCK(wrch);
 1752                         if (wrch->flags & CHN_F_TRIGGERED)
 1753                                 *arg_i |= PCM_ENABLE_OUTPUT;
 1754                         CHN_UNLOCK(wrch);
 1755                 }
 1756                 if (rdch) {
 1757                         CHN_LOCK(rdch);
 1758                         if (rdch->flags & CHN_F_TRIGGERED)
 1759                                 *arg_i |= PCM_ENABLE_INPUT;
 1760                         CHN_UNLOCK(rdch);
 1761                 }
 1762                 break;
 1763 
 1764         case SNDCTL_DSP_GETODELAY:
 1765                 if (wrch) {
 1766                         struct snd_dbuf *bs = wrch->bufsoft;
 1767 
 1768                         CHN_LOCK(wrch);
 1769                         /* XXX abusive DMA update: chn_wrupdate(wrch); */
 1770                         *arg_i = sndbuf_getready(bs);
 1771                         CHN_UNLOCK(wrch);
 1772                 } else
 1773                         ret = EINVAL;
 1774                 break;
 1775 
 1776         case SNDCTL_DSP_POST:
 1777                 if (wrch) {
 1778                         CHN_LOCK(wrch);
 1779                         wrch->flags &= ~CHN_F_NOTRIGGER;
 1780                         chn_start(wrch, 1);
 1781                         CHN_UNLOCK(wrch);
 1782                 }
 1783                 break;
 1784 
 1785         case SNDCTL_DSP_SETDUPLEX:
 1786                 /*
 1787                  * switch to full-duplex mode if card is in half-duplex
 1788                  * mode and is able to work in full-duplex mode
 1789                  */
 1790                 PCM_LOCK(d);
 1791                 if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
 1792                         dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
 1793                 PCM_UNLOCK(d);
 1794                 break;
 1795 
 1796         /*
 1797          * The following four ioctls are simple wrappers around mixer_ioctl
 1798          * with no further processing.  xcmd is short for "translated
 1799          * command".
 1800          */
 1801         case SNDCTL_DSP_GETRECVOL:
 1802                 if (xcmd == 0) {
 1803                         xcmd = SOUND_MIXER_READ_RECLEV;
 1804                         chn = rdch;
 1805                 }
 1806                 /* FALLTHROUGH */
 1807         case SNDCTL_DSP_SETRECVOL:
 1808                 if (xcmd == 0) {
 1809                         xcmd = SOUND_MIXER_WRITE_RECLEV;
 1810                         chn = rdch;
 1811                 }
 1812                 /* FALLTHROUGH */
 1813         case SNDCTL_DSP_GETPLAYVOL:
 1814                 if (xcmd == 0) {
 1815                         xcmd = SOUND_MIXER_READ_PCM;
 1816                         chn = wrch;
 1817                 }
 1818                 /* FALLTHROUGH */
 1819         case SNDCTL_DSP_SETPLAYVOL:
 1820                 if (xcmd == 0) {
 1821                         xcmd = SOUND_MIXER_WRITE_PCM;
 1822                         chn = wrch;
 1823                 }
 1824 
 1825                 ret = dsp_ioctl_channel(i_dev, chn, xcmd, arg);
 1826                 if (ret != -1) {
 1827                         PCM_GIANT_EXIT(d);
 1828                         return (ret);
 1829                 }
 1830 
 1831                 if (d->mixer_dev != NULL) {
 1832                         PCM_ACQUIRE_QUICK(d);
 1833                         ret = mixer_ioctl_cmd(d->mixer_dev, xcmd, arg, -1, td,
 1834                             MIXER_CMD_DIRECT);
 1835                         PCM_RELEASE_QUICK(d);
 1836                 } else
 1837                         ret = ENOTSUP;
 1838 
 1839                 break;
 1840 
 1841         case SNDCTL_DSP_GET_RECSRC_NAMES:
 1842         case SNDCTL_DSP_GET_RECSRC:
 1843         case SNDCTL_DSP_SET_RECSRC:
 1844                 if (d->mixer_dev != NULL) {
 1845                         PCM_ACQUIRE_QUICK(d);
 1846                         ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td,
 1847                             MIXER_CMD_DIRECT);
 1848                         PCM_RELEASE_QUICK(d);
 1849                 } else
 1850                         ret = ENOTSUP;
 1851                 break;
 1852 
 1853         /*
 1854          * The following 3 ioctls aren't very useful at the moment.  For
 1855          * now, only a single channel is associated with a cdev (/dev/dspN
 1856          * instance), so there's only a single output routing to use (i.e.,
 1857          * the wrch bound to this cdev).
 1858          */
 1859         case SNDCTL_DSP_GET_PLAYTGT_NAMES:
 1860                 {
 1861                         oss_mixer_enuminfo *ei;
 1862                         ei = (oss_mixer_enuminfo *)arg;
 1863                         ei->dev = 0;
 1864                         ei->ctrl = 0;
 1865                         ei->version = 0; /* static for now */
 1866                         ei->strindex[0] = 0;
 1867 
 1868                         if (wrch != NULL) {
 1869                                 ei->nvalues = 1;
 1870                                 strlcpy(ei->strings, wrch->name,
 1871                                         sizeof(ei->strings));
 1872                         } else {
 1873                                 ei->nvalues = 0;
 1874                                 ei->strings[0] = '\0';
 1875                         }
 1876                 }
 1877                 break;
 1878         case SNDCTL_DSP_GET_PLAYTGT:
 1879         case SNDCTL_DSP_SET_PLAYTGT:    /* yes, they are the same for now */
 1880                 /*
 1881                  * Re: SET_PLAYTGT
 1882                  *   OSSv4: "The value that was accepted by the device will
 1883                  *   be returned back in the variable pointed by the
 1884                  *   argument."
 1885                  */
 1886                 if (wrch != NULL)
 1887                         *arg_i = 0;
 1888                 else
 1889                         ret = EINVAL;
 1890                 break;
 1891 
 1892         case SNDCTL_DSP_SILENCE:
 1893         /*
 1894          * Flush the software (pre-feed) buffer, but try to minimize playback
 1895          * interruption.  (I.e., record unplayed samples with intent to
 1896          * restore by SNDCTL_DSP_SKIP.) Intended for application "pause"
 1897          * functionality.
 1898          */
 1899                 if (wrch == NULL)
 1900                         ret = EINVAL;
 1901                 else {
 1902                         struct snd_dbuf *bs;
 1903                         CHN_LOCK(wrch);
 1904                         while (wrch->inprog != 0)
 1905                                 cv_wait(&wrch->cv, wrch->lock);
 1906                         bs = wrch->bufsoft;
 1907                         if ((bs->shadbuf != NULL) && (sndbuf_getready(bs) > 0)) {
 1908                                 bs->sl = sndbuf_getready(bs);
 1909                                 sndbuf_dispose(bs, bs->shadbuf, sndbuf_getready(bs));
 1910                                 sndbuf_fillsilence(bs);
 1911                                 chn_start(wrch, 0);
 1912                         }
 1913                         CHN_UNLOCK(wrch);
 1914                 }
 1915                 break;
 1916 
 1917         case SNDCTL_DSP_SKIP:
 1918         /*
 1919          * OSSv4 docs: "This ioctl call discards all unplayed samples in the
 1920          * playback buffer by moving the current write position immediately
 1921          * before the point where the device is currently reading the samples."
 1922          */
 1923                 if (wrch == NULL)
 1924                         ret = EINVAL;
 1925                 else {
 1926                         struct snd_dbuf *bs;
 1927                         CHN_LOCK(wrch);
 1928                         while (wrch->inprog != 0)
 1929                                 cv_wait(&wrch->cv, wrch->lock);
 1930                         bs = wrch->bufsoft;
 1931                         if ((bs->shadbuf != NULL) && (bs->sl > 0)) {
 1932                                 sndbuf_softreset(bs);
 1933                                 sndbuf_acquire(bs, bs->shadbuf, bs->sl);
 1934                                 bs->sl = 0;
 1935                                 chn_start(wrch, 0);
 1936                         }
 1937                         CHN_UNLOCK(wrch);
 1938                 }
 1939                 break;
 1940 
 1941         case SNDCTL_DSP_CURRENT_OPTR:
 1942         case SNDCTL_DSP_CURRENT_IPTR:
 1943         /**
 1944          * @note Changing formats resets the buffer counters, which differs
 1945          *       from the 4Front drivers.  However, I don't expect this to be
 1946          *       much of a problem.
 1947          *
 1948          * @note In a test where @c CURRENT_OPTR is called immediately after write
 1949          *       returns, this driver is about 32K samples behind whereas
 1950          *       4Front's is about 8K samples behind.  Should determine source
 1951          *       of discrepancy, even if only out of curiosity.
 1952          *
 1953          * @todo Actually test SNDCTL_DSP_CURRENT_IPTR.
 1954          */
 1955                 chn = (cmd == SNDCTL_DSP_CURRENT_OPTR) ? wrch : rdch;
 1956                 if (chn == NULL) 
 1957                         ret = EINVAL;
 1958                 else {
 1959                         struct snd_dbuf *bs;
 1960                         /* int tmp; */
 1961 
 1962                         oss_count_t *oc = (oss_count_t *)arg;
 1963 
 1964                         CHN_LOCK(chn);
 1965                         bs = chn->bufsoft;
 1966 #if 0
 1967                         tmp = (sndbuf_getsize(b) + chn_getptr(chn) - sndbuf_gethwptr(b)) % sndbuf_getsize(b);
 1968                         oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getalign(b);
 1969                         oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getalign(b);
 1970 #else
 1971                         oc->samples = sndbuf_gettotal(bs) / sndbuf_getalign(bs);
 1972                         oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getalign(bs);
 1973 #endif
 1974                         CHN_UNLOCK(chn);
 1975                 }
 1976                 break;
 1977 
 1978         case SNDCTL_DSP_HALT_OUTPUT:
 1979         case SNDCTL_DSP_HALT_INPUT:
 1980                 chn = (cmd == SNDCTL_DSP_HALT_OUTPUT) ? wrch : rdch;
 1981                 if (chn == NULL)
 1982                         ret = EINVAL;
 1983                 else {
 1984                         CHN_LOCK(chn);
 1985                         chn_abort(chn);
 1986                         CHN_UNLOCK(chn);
 1987                 }
 1988                 break;
 1989 
 1990         case SNDCTL_DSP_LOW_WATER:
 1991         /*
 1992          * Set the number of bytes required to attract attention by
 1993          * select/poll.
 1994          */
 1995                 if (wrch != NULL) {
 1996                         CHN_LOCK(wrch);
 1997                         wrch->lw = (*arg_i > 1) ? *arg_i : 1;
 1998                         CHN_UNLOCK(wrch);
 1999                 }
 2000                 if (rdch != NULL) {
 2001                         CHN_LOCK(rdch);
 2002                         rdch->lw = (*arg_i > 1) ? *arg_i : 1;
 2003                         CHN_UNLOCK(rdch);
 2004                 }
 2005                 break;
 2006 
 2007         case SNDCTL_DSP_GETERROR:
 2008         /*
 2009          * OSSv4 docs:  "All errors and counters will automatically be
 2010          * cleared to zeroes after the call so each call will return only
 2011          * the errors that occurred after the previous invocation. ... The
 2012          * play_underruns and rec_overrun fields are the only usefull fields
 2013          * returned by OSS 4.0."
 2014          */
 2015                 {
 2016                         audio_errinfo *ei = (audio_errinfo *)arg;
 2017 
 2018                         bzero((void *)ei, sizeof(*ei));
 2019 
 2020                         if (wrch != NULL) {
 2021                                 CHN_LOCK(wrch);
 2022                                 ei->play_underruns = wrch->xruns;
 2023                                 wrch->xruns = 0;
 2024                                 CHN_UNLOCK(wrch);
 2025                         }
 2026                         if (rdch != NULL) {
 2027                                 CHN_LOCK(rdch);
 2028                                 ei->rec_overruns = rdch->xruns;
 2029                                 rdch->xruns = 0;
 2030                                 CHN_UNLOCK(rdch);
 2031                         }
 2032                 }
 2033                 break;
 2034 
 2035         case SNDCTL_DSP_SYNCGROUP:
 2036                 PCM_ACQUIRE_QUICK(d);
 2037                 ret = dsp_oss_syncgroup(wrch, rdch, (oss_syncgroup *)arg);
 2038                 PCM_RELEASE_QUICK(d);
 2039                 break;
 2040 
 2041         case SNDCTL_DSP_SYNCSTART:
 2042                 PCM_ACQUIRE_QUICK(d);
 2043                 ret = dsp_oss_syncstart(*arg_i);
 2044                 PCM_RELEASE_QUICK(d);
 2045                 break;
 2046 
 2047         case SNDCTL_DSP_POLICY:
 2048                 PCM_ACQUIRE_QUICK(d);
 2049                 ret = dsp_oss_policy(wrch, rdch, *arg_i);
 2050                 PCM_RELEASE_QUICK(d);
 2051                 break;
 2052 
 2053         case SNDCTL_DSP_COOKEDMODE:
 2054                 PCM_ACQUIRE_QUICK(d);
 2055                 if (!(dsp_get_flags(i_dev) & SD_F_BITPERFECT))
 2056                         ret = dsp_oss_cookedmode(wrch, rdch, *arg_i);
 2057                 PCM_RELEASE_QUICK(d);
 2058                 break;
 2059         case SNDCTL_DSP_GET_CHNORDER:
 2060                 PCM_ACQUIRE_QUICK(d);
 2061                 ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg);
 2062                 PCM_RELEASE_QUICK(d);
 2063                 break;
 2064         case SNDCTL_DSP_SET_CHNORDER:
 2065                 PCM_ACQUIRE_QUICK(d);
 2066                 ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg);
 2067                 PCM_RELEASE_QUICK(d);
 2068                 break;
 2069         case SNDCTL_DSP_GETCHANNELMASK:         /* XXX vlc */
 2070                 PCM_ACQUIRE_QUICK(d);
 2071                 ret = dsp_oss_getchannelmask(wrch, rdch, (int *)arg);
 2072                 PCM_RELEASE_QUICK(d);
 2073                 break;
 2074         case SNDCTL_DSP_BIND_CHANNEL:           /* XXX what?!? */
 2075                 ret = EINVAL;
 2076                 break;
 2077 #ifdef  OSSV4_EXPERIMENT
 2078         /*
 2079          * XXX The following ioctls are not yet supported and just return
 2080          * EINVAL.
 2081          */
 2082         case SNDCTL_DSP_GETOPEAKS:
 2083         case SNDCTL_DSP_GETIPEAKS:
 2084                 chn = (cmd == SNDCTL_DSP_GETOPEAKS) ? wrch : rdch;
 2085                 if (chn == NULL)
 2086                         ret = EINVAL;
 2087                 else {
 2088                         oss_peaks_t *op = (oss_peaks_t *)arg;
 2089                         int lpeak, rpeak;
 2090 
 2091                         CHN_LOCK(chn);
 2092                         ret = chn_getpeaks(chn, &lpeak, &rpeak);
 2093                         if (ret == -1)
 2094                                 ret = EINVAL;
 2095                         else {
 2096                                 (*op)[0] = lpeak;
 2097                                 (*op)[1] = rpeak;
 2098                         }
 2099                         CHN_UNLOCK(chn);
 2100                 }
 2101                 break;
 2102 
 2103         /*
 2104          * XXX Once implemented, revisit this for proper cv protection
 2105          *     (if necessary).
 2106          */
 2107         case SNDCTL_GETLABEL:
 2108                 ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg);
 2109                 break;
 2110         case SNDCTL_SETLABEL:
 2111                 ret = dsp_oss_setlabel(wrch, rdch, (oss_label_t *)arg);
 2112                 break;
 2113         case SNDCTL_GETSONG:
 2114                 ret = dsp_oss_getsong(wrch, rdch, (oss_longname_t *)arg);
 2115                 break;
 2116         case SNDCTL_SETSONG:
 2117                 ret = dsp_oss_setsong(wrch, rdch, (oss_longname_t *)arg);
 2118                 break;
 2119         case SNDCTL_SETNAME:
 2120                 ret = dsp_oss_setname(wrch, rdch, (oss_longname_t *)arg);
 2121                 break;
 2122 #if 0
 2123         /**
 2124          * @note The S/PDIF interface ioctls, @c SNDCTL_DSP_READCTL and
 2125          * @c SNDCTL_DSP_WRITECTL have been omitted at the suggestion of
 2126          * 4Front Technologies.
 2127          */
 2128         case SNDCTL_DSP_READCTL:
 2129         case SNDCTL_DSP_WRITECTL:
 2130                 ret = EINVAL;
 2131                 break;
 2132 #endif  /* !0 (explicitly omitted ioctls) */
 2133 
 2134 #endif  /* !OSSV4_EXPERIMENT */
 2135         case SNDCTL_DSP_MAPINBUF:
 2136         case SNDCTL_DSP_MAPOUTBUF:
 2137         case SNDCTL_DSP_SETSYNCRO:
 2138                 /* undocumented */
 2139 
 2140         case SNDCTL_DSP_SUBDIVIDE:
 2141         case SOUND_PCM_WRITE_FILTER:
 2142         case SOUND_PCM_READ_FILTER:
 2143                 /* dunno what these do, don't sound important */
 2144 
 2145         default:
 2146                 DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
 2147                 ret = EINVAL;
 2148                 break;
 2149         }
 2150 
 2151         PCM_GIANT_LEAVE(d);
 2152 
 2153         return (ret);
 2154 }
 2155 
 2156 static int
 2157 dsp_poll(struct cdev *i_dev, int events, struct thread *td)
 2158 {
 2159         struct snddev_info *d;
 2160         struct pcm_channel *wrch, *rdch;
 2161         int ret, e;
 2162 
 2163         d = dsp_get_info(i_dev);
 2164         if (!DSP_REGISTERED(d, i_dev))
 2165                 return (EBADF);
 2166 
 2167         PCM_GIANT_ENTER(d);
 2168 
 2169         wrch = NULL;
 2170         rdch = NULL;
 2171         ret = 0;
 2172 
 2173         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 2174 
 2175         if (wrch != NULL && !(wrch->flags & CHN_F_DEAD)) {
 2176                 e = (events & (POLLOUT | POLLWRNORM));
 2177                 if (e)
 2178                         ret |= chn_poll(wrch, e, td);
 2179         }
 2180 
 2181         if (rdch != NULL && !(rdch->flags & CHN_F_DEAD)) {
 2182                 e = (events & (POLLIN | POLLRDNORM));
 2183                 if (e)
 2184                         ret |= chn_poll(rdch, e, td);
 2185         }
 2186 
 2187         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 2188 
 2189         PCM_GIANT_LEAVE(d);
 2190 
 2191         return (ret);
 2192 }
 2193 
 2194 static int
 2195 dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
 2196 {
 2197 
 2198         /* XXX memattr is not honored */
 2199         *paddr = vtophys(offset);
 2200         return (0);
 2201 }
 2202 
 2203 static int
 2204 dsp_mmap_single(struct cdev *i_dev, vm_ooffset_t *offset,
 2205     vm_size_t size, struct vm_object **object, int nprot)
 2206 {
 2207         struct snddev_info *d;
 2208         struct pcm_channel *wrch, *rdch, *c;
 2209 
 2210         /*
 2211          * Reject PROT_EXEC by default. It just doesn't makes sense.
 2212          * Unfortunately, we have to give up this one due to linux_mmap
 2213          * changes.
 2214          *
 2215          * http://lists.freebsd.org/pipermail/freebsd-emulation/2007-June/003698.html
 2216          *
 2217          */
 2218 #ifdef SV_ABI_LINUX
 2219         if ((nprot & PROT_EXEC) && (dsp_mmap_allow_prot_exec < 0 ||
 2220             (dsp_mmap_allow_prot_exec == 0 &&
 2221             SV_CURPROC_ABI() != SV_ABI_LINUX)))
 2222 #else
 2223         if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec < 1)
 2224 #endif
 2225                 return (EINVAL);
 2226 
 2227         /*
 2228          * PROT_READ (alone) selects the input buffer.
 2229          * PROT_WRITE (alone) selects the output buffer.
 2230          * PROT_WRITE|PROT_READ together select the output buffer.
 2231          */
 2232         if ((nprot & (PROT_READ | PROT_WRITE)) == 0)
 2233                 return (EINVAL);
 2234 
 2235         d = dsp_get_info(i_dev);
 2236         if (!DSP_REGISTERED(d, i_dev))
 2237                 return (EINVAL);
 2238 
 2239         PCM_GIANT_ENTER(d);
 2240 
 2241         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 2242 
 2243         c = ((nprot & PROT_WRITE) != 0) ? wrch : rdch;
 2244         if (c == NULL || (c->flags & CHN_F_MMAP_INVALID) ||
 2245             (*offset  + size) > sndbuf_getsize(c->bufsoft) ||
 2246             (wrch != NULL && (wrch->flags & CHN_F_MMAP_INVALID)) ||
 2247             (rdch != NULL && (rdch->flags & CHN_F_MMAP_INVALID))) {
 2248                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 2249                 PCM_GIANT_EXIT(d);
 2250                 return (EINVAL);
 2251         }
 2252 
 2253         if (wrch != NULL)
 2254                 wrch->flags |= CHN_F_MMAP;
 2255         if (rdch != NULL)
 2256                 rdch->flags |= CHN_F_MMAP;
 2257 
 2258         *offset = (uintptr_t)sndbuf_getbufofs(c->bufsoft, *offset);
 2259         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 2260         *object = vm_pager_allocate(OBJT_DEVICE, i_dev,
 2261             size, nprot, *offset, curthread->td_ucred);
 2262 
 2263         PCM_GIANT_LEAVE(d);
 2264 
 2265         if (*object == NULL)
 2266                  return (EINVAL);
 2267         return (0);
 2268 }
 2269 
 2270 /* So much for dev_stdclone() */
 2271 static int
 2272 dsp_stdclone(char *name, char *namep, char *sep, int use_sep, int *u, int *c)
 2273 {
 2274         size_t len;
 2275 
 2276         len = strlen(namep);
 2277 
 2278         if (bcmp(name, namep, len) != 0)
 2279                 return (ENODEV);
 2280 
 2281         name += len;
 2282 
 2283         if (isdigit(*name) == 0)
 2284                 return (ENODEV);
 2285 
 2286         len = strlen(sep);
 2287 
 2288         if (*name == '' && !(name[1] == '\0' || bcmp(name + 1, sep, len) == 0))
 2289                 return (ENODEV);
 2290 
 2291         for (*u = 0; isdigit(*name) != 0; name++) {
 2292                 *u *= 10;
 2293                 *u += *name - '';
 2294                 if (*u > dsp_umax)
 2295                         return (ENODEV);
 2296         }
 2297 
 2298         if (*name == '\0')
 2299                 return ((use_sep == 0) ? 0 : ENODEV);
 2300 
 2301         if (bcmp(name, sep, len) != 0 || isdigit(name[len]) == 0)
 2302                 return (ENODEV);
 2303 
 2304         name += len;
 2305 
 2306         if (*name == '' && name[1] != '\0')
 2307                 return (ENODEV);
 2308 
 2309         for (*c = 0; isdigit(*name) != 0; name++) {
 2310                 *c *= 10;
 2311                 *c += *name - '';
 2312                 if (*c > dsp_cmax)
 2313                         return (ENODEV);
 2314         }
 2315 
 2316         if (*name != '\0')
 2317                 return (ENODEV);
 2318 
 2319         return (0);
 2320 }
 2321 
 2322 static void
 2323 dsp_clone(void *arg,
 2324 #if __FreeBSD_version >= 600034
 2325     struct ucred *cred,
 2326 #endif
 2327     char *name, int namelen, struct cdev **dev)
 2328 {
 2329         struct snddev_info *d;
 2330         struct snd_clone_entry *ce;
 2331         struct pcm_channel *c;
 2332         int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax;
 2333         char *devname, *devcmp, *devsep;
 2334 
 2335         KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!"));
 2336 
 2337         if (*dev != NULL)
 2338                 return;
 2339 
 2340         unit = -1;
 2341         cunit = -1;
 2342         devtype = -1;
 2343         devhw = 0;
 2344         devcmax = -1;
 2345         tumax = -1;
 2346         devname = NULL;
 2347         devsep = NULL;
 2348 
 2349         for (i = 0; unit == -1 &&
 2350             i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) {
 2351                 devtype = dsp_cdevs[i].type;
 2352                 devcmp = dsp_cdevs[i].name;
 2353                 devsep = dsp_cdevs[i].sep;
 2354                 devname = dsp_cdevs[i].alias;
 2355                 if (devname == NULL)
 2356                         devname = devcmp;
 2357                 devhw = dsp_cdevs[i].hw;
 2358                 devcmax = dsp_cdevs[i].max - 1;
 2359                 if (strcmp(name, devcmp) == 0)
 2360                         unit = snd_unit;
 2361                 else if (dsp_stdclone(name, devcmp, devsep,
 2362                     dsp_cdevs[i].use_sep, &unit, &cunit) != 0) {
 2363                         unit = -1;
 2364                         cunit = -1;
 2365                 }
 2366         }
 2367 
 2368         d = devclass_get_softc(pcm_devclass, unit);
 2369         if (!PCM_REGISTERED(d) || d->clones == NULL)
 2370                 return;
 2371 
 2372         /* XXX Need Giant magic entry ??? */
 2373 
 2374         PCM_LOCK(d);
 2375         if (snd_clone_disabled(d->clones)) {
 2376                 PCM_UNLOCK(d);
 2377                 return;
 2378         }
 2379 
 2380         PCM_WAIT(d);
 2381         PCM_ACQUIRE(d);
 2382         PCM_UNLOCK(d);
 2383 
 2384         udcmask = snd_u2unit(unit) | snd_d2unit(devtype);
 2385 
 2386         if (devhw != 0) {
 2387                 KASSERT(devcmax <= dsp_cmax,
 2388                     ("overflow: devcmax=%d, dsp_cmax=%d", devcmax, dsp_cmax));
 2389                 if (cunit > devcmax) {
 2390                         PCM_RELEASE_QUICK(d);
 2391                         return;
 2392                 }
 2393                 udcmask |= snd_c2unit(cunit);
 2394                 CHN_FOREACH(c, d, channels.pcm) {
 2395                         CHN_LOCK(c);
 2396                         if (c->unit != udcmask) {
 2397                                 CHN_UNLOCK(c);
 2398                                 continue;
 2399                         }
 2400                         CHN_UNLOCK(c);
 2401                         udcmask &= ~snd_c2unit(cunit);
 2402                         /*
 2403                          * Temporarily increase clone maxunit to overcome
 2404                          * vchan flexibility.
 2405                          *
 2406                          * # sysctl dev.pcm.0.play.vchans=256
 2407                          * dev.pcm.0.play.vchans: 1 -> 256
 2408                          * # cat /dev/zero > /dev/dsp0.vp255 &
 2409                          * [1] 17296
 2410                          * # sysctl dev.pcm.0.play.vchans=0
 2411                          * dev.pcm.0.play.vchans: 256 -> 1
 2412                          * # fg
 2413                          * [1]  + running    cat /dev/zero > /dev/dsp0.vp255
 2414                          * ^C
 2415                          * # cat /dev/zero > /dev/dsp0.vp255
 2416                          * zsh: operation not supported: /dev/dsp0.vp255
 2417                          */
 2418                         tumax = snd_clone_getmaxunit(d->clones);
 2419                         if (cunit > tumax)
 2420                                 snd_clone_setmaxunit(d->clones, cunit);
 2421                         else
 2422                                 tumax = -1;
 2423                         goto dsp_clone_alloc;
 2424                 }
 2425                 /*
 2426                  * Ok, so we're requesting unallocated vchan, but still
 2427                  * within maximum vchan limit.
 2428                  */
 2429                 if (((devtype == SND_DEV_DSPHW_VPLAY && d->pvchancount > 0) ||
 2430                     (devtype == SND_DEV_DSPHW_VREC && d->rvchancount > 0)) &&
 2431                     cunit < snd_maxautovchans) {
 2432                         udcmask &= ~snd_c2unit(cunit);
 2433                         tumax = snd_clone_getmaxunit(d->clones);
 2434                         if (cunit > tumax)
 2435                                 snd_clone_setmaxunit(d->clones, cunit);
 2436                         else
 2437                                 tumax = -1;
 2438                         goto dsp_clone_alloc;
 2439                 }
 2440                 PCM_RELEASE_QUICK(d);
 2441                 return;
 2442         }
 2443 
 2444 dsp_clone_alloc:
 2445         ce = snd_clone_alloc(d->clones, dev, &cunit, udcmask);
 2446         if (tumax != -1)
 2447                 snd_clone_setmaxunit(d->clones, tumax);
 2448         if (ce != NULL) {
 2449                 udcmask |= snd_c2unit(cunit);
 2450                 *dev = make_dev(&dsp_cdevsw, PCMMINOR(udcmask),
 2451                     UID_ROOT, GID_WHEEL, 0666, "%s%d%s%d",
 2452                     devname, unit, devsep, cunit);
 2453                 snd_clone_register(ce, *dev);
 2454         }
 2455 
 2456         PCM_RELEASE_QUICK(d);
 2457 
 2458         if (*dev != NULL)
 2459                 dev_ref(*dev);
 2460 }
 2461 
 2462 static void
 2463 dsp_sysinit(void *p)
 2464 {
 2465         if (dsp_ehtag != NULL)
 2466                 return;
 2467         /* initialize unit numbering */
 2468         snd_unit_init();
 2469         dsp_umax = PCMMAXUNIT;
 2470         dsp_cmax = PCMMAXCHAN;
 2471         dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
 2472 }
 2473 
 2474 static void
 2475 dsp_sysuninit(void *p)
 2476 {
 2477         if (dsp_ehtag == NULL)
 2478                 return;
 2479         EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
 2480         dsp_ehtag = NULL;
 2481 }
 2482 
 2483 SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
 2484 SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
 2485 
 2486 char *
 2487 dsp_unit2name(char *buf, size_t len, int unit)
 2488 {
 2489         int i, dtype;
 2490 
 2491         KASSERT(buf != NULL && len != 0,
 2492             ("bogus buf=%p len=%ju", buf, (uintmax_t)len));
 2493 
 2494         dtype = snd_unit2d(unit);
 2495 
 2496         for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) {
 2497                 if (dtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL)
 2498                         continue;
 2499                 snprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name,
 2500                     snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit));
 2501                 return (buf);
 2502         }
 2503 
 2504         return (NULL);
 2505 }
 2506 
 2507 /**
 2508  * @brief Handler for SNDCTL_AUDIOINFO.
 2509  *
 2510  * Gathers information about the audio device specified in ai->dev.  If
 2511  * ai->dev == -1, then this function gathers information about the current
 2512  * device.  If the call comes in on a non-audio device and ai->dev == -1,
 2513  * return EINVAL.
 2514  *
 2515  * This routine is supposed to go practically straight to the hardware,
 2516  * getting capabilities directly from the sound card driver, side-stepping
 2517  * the intermediate channel interface.
 2518  *
 2519  * Note, however, that the usefulness of this command is significantly
 2520  * decreased when requesting info about any device other than the one serving
 2521  * the request. While each snddev_channel refers to a specific device node,
 2522  * the converse is *not* true.  Currently, when a sound device node is opened,
 2523  * the sound subsystem scans for an available audio channel (or channels, if
 2524  * opened in read+write) and then assigns them to the si_drv[12] private
 2525  * data fields.  As a result, any information returned linking a channel to
 2526  * a specific character device isn't necessarily accurate.
 2527  *
 2528  * @note
 2529  * Calling threads must not hold any snddev_info or pcm_channel locks.
 2530  * 
 2531  * @param dev           device on which the ioctl was issued
 2532  * @param ai            ioctl request data container
 2533  *
 2534  * @retval 0            success
 2535  * @retval EINVAL       ai->dev specifies an invalid device
 2536  *
 2537  * @todo Verify correctness of Doxygen tags.  ;)
 2538  */
 2539 int
 2540 dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
 2541 {
 2542         struct pcmchan_caps *caps;
 2543         struct pcm_channel *ch;
 2544         struct snddev_info *d;
 2545         uint32_t fmts;
 2546         int i, nchan, *rates, minch, maxch;
 2547         char *devname, buf[CHN_NAMELEN];
 2548 
 2549         /*
 2550          * If probing the device that received the ioctl, make sure it's a
 2551          * DSP device.  (Users may use this ioctl with /dev/mixer and
 2552          * /dev/midi.)
 2553          */
 2554         if (ai->dev == -1 && i_dev->si_devsw != &dsp_cdevsw)
 2555                 return (EINVAL);
 2556 
 2557         ch = NULL;
 2558         devname = NULL;
 2559         nchan = 0;
 2560         bzero(buf, sizeof(buf));
 2561 
 2562         /*
 2563          * Search for the requested audio device (channel).  Start by
 2564          * iterating over pcm devices.
 2565          */ 
 2566         for (i = 0; pcm_devclass != NULL &&
 2567             i < devclass_get_maxunit(pcm_devclass); i++) {
 2568                 d = devclass_get_softc(pcm_devclass, i);
 2569                 if (!PCM_REGISTERED(d))
 2570                         continue;
 2571 
 2572                 /* XXX Need Giant magic entry ??? */
 2573 
 2574                 /* See the note in function docblock */
 2575                 PCM_UNLOCKASSERT(d);
 2576                 PCM_LOCK(d);
 2577 
 2578                 CHN_FOREACH(ch, d, channels.pcm) {
 2579                         CHN_UNLOCKASSERT(ch);
 2580                         CHN_LOCK(ch);
 2581                         if (ai->dev == -1) {
 2582                                 if (DSP_REGISTERED(d, i_dev) &&
 2583                                     (ch == PCM_RDCH(i_dev) ||   /* record ch */
 2584                                     ch == PCM_WRCH(i_dev))) {   /* playback ch */
 2585                                         devname = dsp_unit2name(buf,
 2586                                             sizeof(buf), ch->unit);
 2587                                 }
 2588                         } else if (ai->dev == nchan) {
 2589                                 devname = dsp_unit2name(buf, sizeof(buf),
 2590                                     ch->unit);
 2591                         }
 2592                         if (devname != NULL)
 2593                                 break;
 2594                         CHN_UNLOCK(ch);
 2595                         ++nchan;
 2596                 }
 2597 
 2598                 if (devname != NULL) {
 2599                         /*
 2600                          * At this point, the following synchronization stuff
 2601                          * has happened:
 2602                          * - a specific PCM device is locked.
 2603                          * - a specific audio channel has been locked, so be
 2604                          *   sure to unlock when exiting;
 2605                          */
 2606 
 2607                         caps = chn_getcaps(ch);
 2608 
 2609                         /*
 2610                          * With all handles collected, zero out the user's
 2611                          * container and begin filling in its fields.
 2612                          */
 2613                         bzero((void *)ai, sizeof(oss_audioinfo));
 2614 
 2615                         ai->dev = nchan;
 2616                         strlcpy(ai->name, ch->name,  sizeof(ai->name));
 2617 
 2618                         if ((ch->flags & CHN_F_BUSY) == 0)
 2619                                 ai->busy = 0;
 2620                         else
 2621                                 ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ;
 2622 
 2623                         /**
 2624                          * @note
 2625                          * @c cmd - OSSv4 docs: "Only supported under Linux at
 2626                          *    this moment." Cop-out, I know, but I'll save
 2627                          *    running around in the process table for later.
 2628                          *    Is there a risk of leaking information?
 2629                          */
 2630                         ai->pid = ch->pid;
 2631 
 2632                         /*
 2633                          * These flags stolen from SNDCTL_DSP_GETCAPS handler.
 2634                          * Note, however, that a single channel operates in
 2635                          * only one direction, so PCM_CAP_DUPLEX is out.
 2636                          */
 2637                         /**
 2638                          * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep
 2639                          *       these in pcmchan::caps?
 2640                          */
 2641                         ai->caps = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER |
 2642                             ((ch->direction == PCMDIR_PLAY) ? PCM_CAP_OUTPUT : PCM_CAP_INPUT);
 2643 
 2644                         /*
 2645                          * Collect formats supported @b natively by the
 2646                          * device.  Also determine min/max channels.  (I.e.,
 2647                          * mono, stereo, or both?)
 2648                          *
 2649                          * If any channel is stereo, maxch = 2;
 2650                          * if all channels are stereo, minch = 2, too;
 2651                          * if any channel is mono, minch = 1;
 2652                          * and if all channels are mono, maxch = 1.
 2653                          */
 2654                         minch = 0;
 2655                         maxch = 0;
 2656                         fmts = 0;
 2657                         for (i = 0; caps->fmtlist[i]; i++) {
 2658                                 fmts |= caps->fmtlist[i];
 2659                                 if (AFMT_CHANNEL(caps->fmtlist[i]) > 1) {
 2660                                         minch = (minch == 0) ? 2 : minch;
 2661                                         maxch = 2;
 2662                                 } else {
 2663                                         minch = 1;
 2664                                         maxch = (maxch == 0) ? 1 : maxch;
 2665                                 }
 2666                         }
 2667 
 2668                         if (ch->direction == PCMDIR_PLAY)
 2669                                 ai->oformats = fmts;
 2670                         else
 2671                                 ai->iformats = fmts;
 2672 
 2673                         /**
 2674                          * @note
 2675                          * @c magic - OSSv4 docs: "Reserved for internal use
 2676                          *    by OSS."
 2677                          *
 2678                          * @par
 2679                          * @c card_number - OSSv4 docs: "Number of the sound
 2680                          *    card where this device belongs or -1 if this
 2681                          *    information is not available.  Applications
 2682                          *    should normally not use this field for any
 2683                          *    purpose."
 2684                          */
 2685                         ai->card_number = -1;
 2686                         /**
 2687                          * @todo @c song_name - depends first on
 2688                          *          SNDCTL_[GS]ETSONG @todo @c label - depends
 2689                          *          on SNDCTL_[GS]ETLABEL
 2690                          * @todo @c port_number - routing information?
 2691                          */
 2692                         ai->port_number = -1;
 2693                         ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1;
 2694                         /**
 2695                          * @note
 2696                          * @c real_device - OSSv4 docs:  "Obsolete."
 2697                          */
 2698                         ai->real_device = -1;
 2699                         strlcpy(ai->devnode, "/dev/", sizeof(ai->devnode));
 2700                         strlcat(ai->devnode, devname, sizeof(ai->devnode));
 2701                         ai->enabled = device_is_attached(d->dev) ? 1 : 0;
 2702                         /**
 2703                          * @note
 2704                          * @c flags - OSSv4 docs: "Reserved for future use."
 2705                          *
 2706                          * @note
 2707                          * @c binding - OSSv4 docs: "Reserved for future use."
 2708                          *
 2709                          * @todo @c handle - haven't decided how to generate
 2710                          *       this yet; bus, vendor, device IDs?
 2711                          */
 2712                         ai->min_rate = caps->minspeed;
 2713                         ai->max_rate = caps->maxspeed;
 2714 
 2715                         ai->min_channels = minch;
 2716                         ai->max_channels = maxch;
 2717 
 2718                         ai->nrates = chn_getrates(ch, &rates);
 2719                         if (ai->nrates > OSS_MAX_SAMPLE_RATES)
 2720                                 ai->nrates = OSS_MAX_SAMPLE_RATES;
 2721 
 2722                         for (i = 0; i < ai->nrates; i++)
 2723                                 ai->rates[i] = rates[i];
 2724                         
 2725                         ai->next_play_engine = 0;
 2726                         ai->next_rec_engine = 0;
 2727 
 2728                         CHN_UNLOCK(ch);
 2729                 }
 2730 
 2731                 PCM_UNLOCK(d);
 2732 
 2733                 if (devname != NULL)
 2734                         return (0);
 2735         }
 2736 
 2737         /* Exhausted the search -- nothing is locked, so return. */
 2738         return (EINVAL);
 2739 }
 2740 
 2741 /**
 2742  * @brief Assigns a PCM channel to a sync group.
 2743  *
 2744  * Sync groups are used to enable audio operations on multiple devices
 2745  * simultaneously.  They may be used with any number of devices and may
 2746  * span across applications.  Devices are added to groups with
 2747  * the SNDCTL_DSP_SYNCGROUP ioctl, and operations are triggered with the
 2748  * SNDCTL_DSP_SYNCSTART ioctl.
 2749  *
 2750  * If the @c id field of the @c group parameter is set to zero, then a new
 2751  * sync group is created.  Otherwise, wrch and rdch (if set) are added to
 2752  * the group specified.
 2753  *
 2754  * @todo As far as memory allocation, should we assume that things are
 2755  *       okay and allocate with M_WAITOK before acquiring channel locks,
 2756  *       freeing later if not?
 2757  *
 2758  * @param wrch  output channel associated w/ device (if any)
 2759  * @param rdch  input channel associated w/ device (if any)
 2760  * @param group Sync group parameters
 2761  *
 2762  * @retval 0            success
 2763  * @retval non-zero     error to be propagated upstream
 2764  */
 2765 static int
 2766 dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group)
 2767 {
 2768         struct pcmchan_syncmember *smrd, *smwr;
 2769         struct pcmchan_syncgroup *sg;
 2770         int ret, sg_ids[3];
 2771 
 2772         smrd = NULL;
 2773         smwr = NULL;
 2774         sg = NULL;
 2775         ret = 0;
 2776 
 2777         /*
 2778          * Free_unr() may sleep, so store released syncgroup IDs until after
 2779          * all locks are released.
 2780          */
 2781         sg_ids[0] = sg_ids[1] = sg_ids[2] = 0;
 2782 
 2783         PCM_SG_LOCK();
 2784 
 2785         /*
 2786          * - Insert channel(s) into group's member list.
 2787          * - Set CHN_F_NOTRIGGER on channel(s).
 2788          * - Stop channel(s).  
 2789          */
 2790 
 2791         /*
 2792          * If device's channels are already mapped to a group, unmap them.
 2793          */
 2794         if (wrch) {
 2795                 CHN_LOCK(wrch);
 2796                 sg_ids[0] = chn_syncdestroy(wrch);
 2797         }
 2798 
 2799         if (rdch) {
 2800                 CHN_LOCK(rdch);
 2801                 sg_ids[1] = chn_syncdestroy(rdch);
 2802         }
 2803 
 2804         /*
 2805          * Verify that mode matches character device properites.
 2806          *  - Bail if PCM_ENABLE_OUTPUT && wrch == NULL.
 2807          *  - Bail if PCM_ENABLE_INPUT && rdch == NULL.
 2808          */
 2809         if (((wrch == NULL) && (group->mode & PCM_ENABLE_OUTPUT)) ||
 2810             ((rdch == NULL) && (group->mode & PCM_ENABLE_INPUT))) {
 2811                 ret = EINVAL;
 2812                 goto out;
 2813         }
 2814 
 2815         /*
 2816          * An id of zero indicates the user wants to create a new
 2817          * syncgroup.
 2818          */
 2819         if (group->id == 0) {
 2820                 sg = (struct pcmchan_syncgroup *)malloc(sizeof(*sg), M_DEVBUF, M_NOWAIT);
 2821                 if (sg != NULL) {
 2822                         SLIST_INIT(&sg->members);
 2823                         sg->id = alloc_unr(pcmsg_unrhdr);
 2824 
 2825                         group->id = sg->id;
 2826                         SLIST_INSERT_HEAD(&snd_pcm_syncgroups, sg, link);
 2827                 } else
 2828                         ret = ENOMEM;
 2829         } else {
 2830                 SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) {
 2831                         if (sg->id == group->id)
 2832                                 break;
 2833                 }
 2834                 if (sg == NULL)
 2835                         ret = EINVAL;
 2836         }
 2837 
 2838         /* Couldn't create or find a syncgroup.  Fail. */
 2839         if (sg == NULL)
 2840                 goto out;
 2841 
 2842         /*
 2843          * Allocate a syncmember, assign it and a channel together, and
 2844          * insert into syncgroup.
 2845          */
 2846         if (group->mode & PCM_ENABLE_INPUT) {
 2847                 smrd = (struct pcmchan_syncmember *)malloc(sizeof(*smrd), M_DEVBUF, M_NOWAIT);
 2848                 if (smrd == NULL) {
 2849                         ret = ENOMEM;
 2850                         goto out;
 2851                 }
 2852 
 2853                 SLIST_INSERT_HEAD(&sg->members, smrd, link);
 2854                 smrd->parent = sg;
 2855                 smrd->ch = rdch;
 2856 
 2857                 chn_abort(rdch);
 2858                 rdch->flags |= CHN_F_NOTRIGGER;
 2859                 rdch->sm = smrd;
 2860         }
 2861 
 2862         if (group->mode & PCM_ENABLE_OUTPUT) {
 2863                 smwr = (struct pcmchan_syncmember *)malloc(sizeof(*smwr), M_DEVBUF, M_NOWAIT);
 2864                 if (smwr == NULL) {
 2865                         ret = ENOMEM;
 2866                         goto out;
 2867                 }
 2868 
 2869                 SLIST_INSERT_HEAD(&sg->members, smwr, link);
 2870                 smwr->parent = sg;
 2871                 smwr->ch = wrch;
 2872 
 2873                 chn_abort(wrch);
 2874                 wrch->flags |= CHN_F_NOTRIGGER;
 2875                 wrch->sm = smwr;
 2876         }
 2877 
 2878 
 2879 out:
 2880         if (ret != 0) {
 2881                 if (smrd != NULL)
 2882                         free(smrd, M_DEVBUF);
 2883                 if ((sg != NULL) && SLIST_EMPTY(&sg->members)) {
 2884                         sg_ids[2] = sg->id;
 2885                         SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
 2886                         free(sg, M_DEVBUF);
 2887                 }
 2888 
 2889                 if (wrch)
 2890                         wrch->sm = NULL;
 2891                 if (rdch)
 2892                         rdch->sm = NULL;
 2893         }
 2894 
 2895         if (wrch)
 2896                 CHN_UNLOCK(wrch);
 2897         if (rdch)
 2898                 CHN_UNLOCK(rdch);
 2899 
 2900         PCM_SG_UNLOCK();
 2901 
 2902         if (sg_ids[0])
 2903                 free_unr(pcmsg_unrhdr, sg_ids[0]);
 2904         if (sg_ids[1])
 2905                 free_unr(pcmsg_unrhdr, sg_ids[1]);
 2906         if (sg_ids[2])
 2907                 free_unr(pcmsg_unrhdr, sg_ids[2]);
 2908 
 2909         return (ret);
 2910 }
 2911 
 2912 /**
 2913  * @brief Launch a sync group into action
 2914  *
 2915  * Sync groups are established via SNDCTL_DSP_SYNCGROUP.  This function
 2916  * iterates over all members, triggering them along the way.
 2917  *
 2918  * @note Caller must not hold any channel locks.
 2919  *
 2920  * @param sg_id sync group identifier
 2921  *
 2922  * @retval 0    success
 2923  * @retval non-zero     error worthy of propagating upstream to user
 2924  */
 2925 static int
 2926 dsp_oss_syncstart(int sg_id)
 2927 {
 2928         struct pcmchan_syncmember *sm, *sm_tmp;
 2929         struct pcmchan_syncgroup *sg;
 2930         struct pcm_channel *c;
 2931         int ret, needlocks;
 2932 
 2933         /* Get the synclists lock */
 2934         PCM_SG_LOCK();
 2935 
 2936         do {
 2937                 ret = 0;
 2938                 needlocks = 0;
 2939 
 2940                 /* Search for syncgroup by ID */
 2941                 SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) {
 2942                         if (sg->id == sg_id)
 2943                                 break;
 2944                 }
 2945 
 2946                 /* Return EINVAL if not found */
 2947                 if (sg == NULL) {
 2948                         ret = EINVAL;
 2949                         break;
 2950                 }
 2951 
 2952                 /* Any removals resulting in an empty group should've handled this */
 2953                 KASSERT(!SLIST_EMPTY(&sg->members), ("found empty syncgroup"));
 2954 
 2955                 /*
 2956                  * Attempt to lock all member channels - if any are already
 2957                  * locked, unlock those acquired, sleep for a bit, and try
 2958                  * again.
 2959                  */
 2960                 SLIST_FOREACH(sm, &sg->members, link) {
 2961                         if (CHN_TRYLOCK(sm->ch) == 0) {
 2962                                 int timo = hz * 5/1000; 
 2963                                 if (timo < 1)
 2964                                         timo = 1;
 2965 
 2966                                 /* Release all locked channels so far, retry */
 2967                                 SLIST_FOREACH(sm_tmp, &sg->members, link) {
 2968                                         /* sm is the member already locked */
 2969                                         if (sm == sm_tmp)
 2970                                                 break;
 2971                                         CHN_UNLOCK(sm_tmp->ch);
 2972                                 }
 2973 
 2974                                 /** @todo Is PRIBIO correct/ */
 2975                                 ret = msleep(sm, &snd_pcm_syncgroups_mtx,
 2976                                     PRIBIO | PCATCH, "pcmsg", timo);
 2977                                 if (ret == EINTR || ret == ERESTART)
 2978                                         break;
 2979 
 2980                                 needlocks = 1;
 2981                                 ret = 0; /* Assumes ret == EAGAIN... */
 2982                         }
 2983                 }
 2984         } while (needlocks && ret == 0);
 2985 
 2986         /* Proceed only if no errors encountered. */
 2987         if (ret == 0) {
 2988                 /* Launch channels */
 2989                 while ((sm = SLIST_FIRST(&sg->members)) != NULL) {
 2990                         SLIST_REMOVE_HEAD(&sg->members, link);
 2991 
 2992                         c = sm->ch;
 2993                         c->sm = NULL;
 2994                         chn_start(c, 1);
 2995                         c->flags &= ~CHN_F_NOTRIGGER;
 2996                         CHN_UNLOCK(c);
 2997 
 2998                         free(sm, M_DEVBUF);
 2999                 }
 3000 
 3001                 SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
 3002                 free(sg, M_DEVBUF);
 3003         }
 3004 
 3005         PCM_SG_UNLOCK();
 3006 
 3007         /*
 3008          * Free_unr() may sleep, so be sure to give up the syncgroup lock
 3009          * first.
 3010          */
 3011         if (ret == 0)
 3012                 free_unr(pcmsg_unrhdr, sg_id);
 3013 
 3014         return (ret);
 3015 }
 3016 
 3017 /**
 3018  * @brief Handler for SNDCTL_DSP_POLICY
 3019  *
 3020  * The SNDCTL_DSP_POLICY ioctl is a simpler interface to control fragment
 3021  * size and count like with SNDCTL_DSP_SETFRAGMENT.  Instead of the user
 3022  * specifying those two parameters, s/he simply selects a number from 0..10
 3023  * which corresponds to a buffer size.  Smaller numbers request smaller
 3024  * buffers with lower latencies (at greater overhead from more frequent
 3025  * interrupts), while greater numbers behave in the opposite manner.
 3026  *
 3027  * The 4Front spec states that a value of 5 should be the default.  However,
 3028  * this implementation deviates slightly by using a linear scale without
 3029  * consulting drivers.  I.e., even though drivers may have different default
 3030  * buffer sizes, a policy argument of 5 will have the same result across
 3031  * all drivers.
 3032  *
 3033  * See http://manuals.opensound.com/developer/SNDCTL_DSP_POLICY.html for
 3034  * more information.
 3035  *
 3036  * @todo When SNDCTL_DSP_COOKEDMODE is supported, it'll be necessary to
 3037  *       work with hardware drivers directly.
 3038  *
 3039  * @note PCM channel arguments must not be locked by caller.
 3040  *
 3041  * @param wrch  Pointer to opened playback channel (optional; may be NULL)
 3042  * @param rdch  " recording channel (optional; may be NULL)
 3043  * @param policy Integer from [0:10]
 3044  *
 3045  * @retval 0    constant (for now)
 3046  */
 3047 static int
 3048 dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy)
 3049 {
 3050         int ret;
 3051 
 3052         if (policy < CHN_POLICY_MIN || policy > CHN_POLICY_MAX)
 3053                 return (EIO);
 3054 
 3055         /* Default: success */
 3056         ret = 0;
 3057 
 3058         if (rdch) {
 3059                 CHN_LOCK(rdch);
 3060                 ret = chn_setlatency(rdch, policy);
 3061                 CHN_UNLOCK(rdch);
 3062         }
 3063 
 3064         if (wrch && ret == 0) {
 3065                 CHN_LOCK(wrch);
 3066                 ret = chn_setlatency(wrch, policy);
 3067                 CHN_UNLOCK(wrch);
 3068         }
 3069 
 3070         if (ret)
 3071                 ret = EIO;
 3072 
 3073         return (ret);
 3074 }
 3075 
 3076 /**
 3077  * @brief Enable or disable "cooked" mode
 3078  *
 3079  * This is a handler for @c SNDCTL_DSP_COOKEDMODE.  When in cooked mode, which
 3080  * is the default, the sound system handles rate and format conversions
 3081  * automatically (ex: user writing 11025Hz/8 bit/unsigned but card only
 3082  * operates with 44100Hz/16bit/signed samples).
 3083  *
 3084  * Disabling cooked mode is intended for applications wanting to mmap()
 3085  * a sound card's buffer space directly, bypassing the FreeBSD 2-stage
 3086  * feeder architecture, presumably to gain as much control over audio
 3087  * hardware as possible.
 3088  *
 3089  * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html
 3090  * for more details.
 3091  *
 3092  * @param wrch          playback channel (optional; may be NULL)
 3093  * @param rdch          recording channel (optional; may be NULL)
 3094  * @param enabled       0 = raw mode, 1 = cooked mode
 3095  *
 3096  * @retval EINVAL       Operation not yet supported.
 3097  */
 3098 static int
 3099 dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled)
 3100 {
 3101 
 3102         /*
 3103          * XXX I just don't get it. Why don't they call it
 3104          * "BITPERFECT" ~ SNDCTL_DSP_BITPERFECT !?!?.
 3105          * This is just plain so confusing, incoherent,
 3106          * <insert any non-printable characters here>.
 3107          */
 3108         if (!(enabled == 1 || enabled == 0))
 3109                 return (EINVAL);
 3110 
 3111         /*
 3112          * I won't give in. I'm inverting its logic here and now.
 3113          * Brag all you want, but "BITPERFECT" should be the better
 3114          * term here.
 3115          */
 3116         enabled ^= 0x00000001;
 3117 
 3118         if (wrch != NULL) {
 3119                 CHN_LOCK(wrch);
 3120                 wrch->flags &= ~CHN_F_BITPERFECT;
 3121                 wrch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000;
 3122                 CHN_UNLOCK(wrch);
 3123         }
 3124 
 3125         if (rdch != NULL) {
 3126                 CHN_LOCK(rdch);
 3127                 rdch->flags &= ~CHN_F_BITPERFECT;
 3128                 rdch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000;
 3129                 CHN_UNLOCK(rdch);
 3130         }
 3131 
 3132         return (0);
 3133 }
 3134 
 3135 /**
 3136  * @brief Retrieve channel interleaving order
 3137  *
 3138  * This is the handler for @c SNDCTL_DSP_GET_CHNORDER.
 3139  *
 3140  * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_GET_CHNORDER.html
 3141  * for more details.
 3142  *
 3143  * @note As the ioctl definition is still under construction, FreeBSD
 3144  *       does not currently support SNDCTL_DSP_GET_CHNORDER.
 3145  *
 3146  * @param wrch  playback channel (optional; may be NULL)
 3147  * @param rdch  recording channel (optional; may be NULL)
 3148  * @param map   channel map (result will be stored there)
 3149  *
 3150  * @retval EINVAL       Operation not yet supported.
 3151  */
 3152 static int
 3153 dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map)
 3154 {
 3155         struct pcm_channel *ch;
 3156         int ret;
 3157 
 3158         ch = (wrch != NULL) ? wrch : rdch;
 3159         if (ch != NULL) {
 3160                 CHN_LOCK(ch);
 3161                 ret = chn_oss_getorder(ch, map);
 3162                 CHN_UNLOCK(ch);
 3163         } else
 3164                 ret = EINVAL;
 3165 
 3166         return (ret);
 3167 }
 3168 
 3169 /**
 3170  * @brief Specify channel interleaving order
 3171  *
 3172  * This is the handler for @c SNDCTL_DSP_SET_CHNORDER.
 3173  *
 3174  * @note As the ioctl definition is still under construction, FreeBSD
 3175  *       does not currently support @c SNDCTL_DSP_SET_CHNORDER.
 3176  *
 3177  * @param wrch  playback channel (optional; may be NULL)
 3178  * @param rdch  recording channel (optional; may be NULL)
 3179  * @param map   channel map
 3180  *
 3181  * @retval EINVAL       Operation not yet supported.
 3182  */
 3183 static int
 3184 dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map)
 3185 {
 3186         int ret;
 3187 
 3188         ret = 0;
 3189 
 3190         if (wrch != NULL) {
 3191                 CHN_LOCK(wrch);
 3192                 ret = chn_oss_setorder(wrch, map);
 3193                 CHN_UNLOCK(wrch);
 3194         }
 3195 
 3196         if (ret == 0 && rdch != NULL) {
 3197                 CHN_LOCK(rdch);
 3198                 ret = chn_oss_setorder(rdch, map);
 3199                 CHN_UNLOCK(rdch);
 3200         }
 3201 
 3202         return (ret);
 3203 }
 3204 
 3205 static int
 3206 dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch,
 3207     int *mask)
 3208 {
 3209         struct pcm_channel *ch;
 3210         uint32_t chnmask;
 3211         int ret;
 3212 
 3213         chnmask = 0;
 3214         ch = (wrch != NULL) ? wrch : rdch;
 3215 
 3216         if (ch != NULL) {
 3217                 CHN_LOCK(ch);
 3218                 ret = chn_oss_getmask(ch, &chnmask);
 3219                 CHN_UNLOCK(ch);
 3220         } else
 3221                 ret = EINVAL;
 3222 
 3223         if (ret == 0)
 3224                 *mask = chnmask;
 3225 
 3226         return (ret);
 3227 }
 3228 
 3229 #ifdef OSSV4_EXPERIMENT
 3230 /**
 3231  * @brief Retrieve an audio device's label
 3232  *
 3233  * This is a handler for the @c SNDCTL_GETLABEL ioctl.
 3234  *
 3235  * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html
 3236  * for more details.
 3237  *
 3238  * From Hannu@4Front:  "For example ossxmix (just like some HW mixer
 3239  * consoles) can show variable "labels" for certain controls. By default
 3240  * the application name (say quake) is shown as the label but
 3241  * applications may change the labels themselves."
 3242  *
 3243  * @note As the ioctl definition is still under construction, FreeBSD
 3244  *       does not currently support @c SNDCTL_GETLABEL.
 3245  *
 3246  * @param wrch  playback channel (optional; may be NULL)
 3247  * @param rdch  recording channel (optional; may be NULL)
 3248  * @param label label gets copied here
 3249  *
 3250  * @retval EINVAL       Operation not yet supported.
 3251  */
 3252 static int
 3253 dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label)
 3254 {
 3255         return (EINVAL);
 3256 }
 3257 
 3258 /**
 3259  * @brief Specify an audio device's label
 3260  *
 3261  * This is a handler for the @c SNDCTL_SETLABEL ioctl.  Please see the
 3262  * comments for @c dsp_oss_getlabel immediately above.
 3263  *
 3264  * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html
 3265  * for more details.
 3266  *
 3267  * @note As the ioctl definition is still under construction, FreeBSD
 3268  *       does not currently support SNDCTL_SETLABEL.
 3269  *
 3270  * @param wrch  playback channel (optional; may be NULL)
 3271  * @param rdch  recording channel (optional; may be NULL)
 3272  * @param label label gets copied from here
 3273  *
 3274  * @retval EINVAL       Operation not yet supported.
 3275  */
 3276 static int
 3277 dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label)
 3278 {
 3279         return (EINVAL);
 3280 }
 3281 
 3282 /**
 3283  * @brief Retrieve name of currently played song
 3284  *
 3285  * This is a handler for the @c SNDCTL_GETSONG ioctl.  Audio players could
 3286  * tell the system the name of the currently playing song, which would be
 3287  * visible in @c /dev/sndstat.
 3288  *
 3289  * See @c http://manuals.opensound.com/developer/SNDCTL_GETSONG.html
 3290  * for more details.
 3291  *
 3292  * @note As the ioctl definition is still under construction, FreeBSD
 3293  *       does not currently support SNDCTL_GETSONG.
 3294  *
 3295  * @param wrch  playback channel (optional; may be NULL)
 3296  * @param rdch  recording channel (optional; may be NULL)
 3297  * @param song  song name gets copied here
 3298  *
 3299  * @retval EINVAL       Operation not yet supported.
 3300  */
 3301 static int
 3302 dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song)
 3303 {
 3304         return (EINVAL);
 3305 }
 3306 
 3307 /**
 3308  * @brief Retrieve name of currently played song
 3309  *
 3310  * This is a handler for the @c SNDCTL_SETSONG ioctl.  Audio players could
 3311  * tell the system the name of the currently playing song, which would be
 3312  * visible in @c /dev/sndstat.
 3313  *
 3314  * See @c http://manuals.opensound.com/developer/SNDCTL_SETSONG.html
 3315  * for more details.
 3316  *
 3317  * @note As the ioctl definition is still under construction, FreeBSD
 3318  *       does not currently support SNDCTL_SETSONG.
 3319  *
 3320  * @param wrch  playback channel (optional; may be NULL)
 3321  * @param rdch  recording channel (optional; may be NULL)
 3322  * @param song  song name gets copied from here
 3323  *
 3324  * @retval EINVAL       Operation not yet supported.
 3325  */
 3326 static int
 3327 dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song)
 3328 {
 3329         return (EINVAL);
 3330 }
 3331 
 3332 /**
 3333  * @brief Rename a device
 3334  *
 3335  * This is a handler for the @c SNDCTL_SETNAME ioctl.
 3336  *
 3337  * See @c http://manuals.opensound.com/developer/SNDCTL_SETNAME.html for
 3338  * more details.
 3339  *
 3340  * From Hannu@4Front:  "This call is used to change the device name
 3341  * reported in /dev/sndstat and ossinfo. So instead of  using some generic
 3342  * 'OSS loopback audio (MIDI) driver' the device may be given a meaningfull
 3343  * name depending on the current context (for example 'OSS virtual wave table
 3344  * synth' or 'VoIP link to London')."
 3345  *
 3346  * @note As the ioctl definition is still under construction, FreeBSD
 3347  *       does not currently support SNDCTL_SETNAME.
 3348  *
 3349  * @param wrch  playback channel (optional; may be NULL)
 3350  * @param rdch  recording channel (optional; may be NULL)
 3351  * @param name  new device name gets copied from here
 3352  *
 3353  * @retval EINVAL       Operation not yet supported.
 3354  */
 3355 static int
 3356 dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name)
 3357 {
 3358         return (EINVAL);
 3359 }
 3360 #endif  /* !OSSV4_EXPERIMENT */

Cache object: c83426819fe9177cc1fe1a282aee09c0


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