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) 1999 Cameron Grant <cg@freebsd.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  *
   26  * $FreeBSD: src/sys/dev/sound/pcm/dsp.c,v 1.80.2.6 2006/04/04 17:43:48 ariff Exp $
   27  */
   28 
   29 #include <sys/param.h>
   30 #include <sys/queue.h>
   31 #include <sys/event.h>
   32 
   33 #include <dev/sound/pcm/dsp.h>
   34 #include <dev/sound/pcm/sound.h>
   35 
   36 
   37 #define OLDPCM_IOCTL
   38 
   39 static d_open_t dsp_open;
   40 static d_close_t dsp_close;
   41 static d_read_t dsp_read;
   42 static d_write_t dsp_write;
   43 static d_ioctl_t dsp_ioctl;
   44 static d_kqfilter_t dsp_kqfilter;
   45 static d_mmap_t dsp_mmap;
   46 
   47 static void dsp_filter_detach(struct knote *);
   48 static int dsp_filter_read(struct knote *, long);
   49 static int dsp_filter_write(struct knote *, long);
   50 
   51 struct dev_ops dsp_ops = {
   52         { "dsp", 0, 0},
   53         /*.d_flags =    D_NEEDGIANT,*/
   54         .d_open =       dsp_open,
   55         .d_close =      dsp_close,
   56         .d_read =       dsp_read,
   57         .d_write =      dsp_write,
   58         .d_ioctl =      dsp_ioctl,
   59         .d_kqfilter =   dsp_kqfilter,
   60         .d_mmap =       dsp_mmap,
   61 };
   62 
   63 struct snddev_info *
   64 dsp_get_info(struct cdev *dev)
   65 {
   66         struct snddev_info *d;
   67         int unit;
   68 
   69         unit = PCMUNIT(dev);
   70         if (unit >= devclass_get_maxunit(pcm_devclass))
   71                 return NULL;
   72         d = devclass_get_softc(pcm_devclass, unit);
   73 
   74         return d;
   75 }
   76 
   77 static u_int32_t
   78 dsp_get_flags(struct cdev *dev)
   79 {
   80         device_t bdev;
   81         int unit;
   82 
   83         unit = PCMUNIT(dev);
   84         if (unit >= devclass_get_maxunit(pcm_devclass))
   85                 return 0xffffffff;
   86         bdev = devclass_get_device(pcm_devclass, unit);
   87 
   88         return pcm_getflags(bdev);
   89 }
   90 
   91 static void
   92 dsp_set_flags(struct cdev *dev, u_int32_t flags)
   93 {
   94         device_t bdev;
   95         int unit;
   96 
   97         unit = PCMUNIT(dev);
   98         if (unit >= devclass_get_maxunit(pcm_devclass))
   99                 return;
  100         bdev = devclass_get_device(pcm_devclass, unit);
  101 
  102         pcm_setflags(bdev, flags);
  103 }
  104 
  105 /*
  106  * return the channels associated with an open device instance.
  107  * set the priority if the device is simplex and one direction (only) is
  108  * specified.
  109  * lock channels specified.
  110  */
  111 static int
  112 getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
  113 {
  114         struct snddev_info *d;
  115         u_int32_t flags;
  116 
  117         flags = dsp_get_flags(dev);
  118         d = dsp_get_info(dev);
  119         pcm_inprog(d, 1);
  120         pcm_lock(d);
  121         KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
  122                 ("getchns: read and write both prioritised"));
  123 
  124         if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
  125                 flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
  126                 dsp_set_flags(dev, flags);
  127         }
  128 
  129         *rdch = dev->si_drv1;
  130         *wrch = dev->si_drv2;
  131         if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
  132                 if (prio) {
  133                         if (*rdch && flags & SD_F_PRIO_WR) {
  134                                 dev->si_drv1 = NULL;
  135                                 *rdch = pcm_getfakechan(d);
  136                         } else if (*wrch && flags & SD_F_PRIO_RD) {
  137                                 dev->si_drv2 = NULL;
  138                                 *wrch = pcm_getfakechan(d);
  139                         }
  140                 }
  141 
  142                 pcm_getfakechan(d)->flags |= CHN_F_BUSY;
  143         }
  144         pcm_unlock(d);
  145 
  146         if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
  147                 CHN_LOCK(*rdch);
  148         if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
  149                 CHN_LOCK(*wrch);
  150 
  151         return 0;
  152 }
  153 
  154 /* unlock specified channels */
  155 static void
  156 relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
  157 {
  158         struct snddev_info *d;
  159 
  160         d = dsp_get_info(dev);
  161         if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
  162                 CHN_UNLOCK(wrch);
  163         if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
  164                 CHN_UNLOCK(rdch);
  165         pcm_inprog(d, -1);
  166 }
  167 
  168 static int
  169 dsp_open(struct dev_open_args *ap)
  170 {
  171         struct cdev *i_dev = ap->a_head.a_dev;
  172         struct thread *td = curthread;
  173         int flags = ap->a_oflags;
  174         struct pcm_channel *rdch, *wrch;
  175         struct snddev_info *d = NULL;
  176         struct snddev_channel *sce = NULL;
  177         u_int32_t fmt = AFMT_U8;
  178         int error;
  179         int chnum;
  180 
  181         if (i_dev == NULL) {
  182                 error = ENODEV;
  183                 goto out;
  184         }
  185 
  186         d = dsp_get_info(i_dev);
  187         SLIST_FOREACH(sce, &d->channels, link) {
  188                 if (sce->dsp_dev == i_dev)
  189                         break;
  190         }
  191 
  192         if (sce == NULL) {
  193                 error = ENODEV;
  194                 goto out;
  195         }
  196 
  197         if (td == NULL) {
  198                 error = ENODEV;
  199                 goto out;
  200         }
  201 
  202         if ((flags & (FREAD | FWRITE)) == 0) {
  203                 error = EINVAL;
  204                 goto out;
  205         }
  206 
  207         chnum = PCMCHAN(i_dev);
  208 
  209         /* lock snddev so nobody else can monkey with it */
  210         pcm_lock(d);
  211 
  212         rdch = i_dev->si_drv1;
  213         wrch = i_dev->si_drv2;
  214 
  215         if (rdch || wrch || ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) &&
  216                     (flags & (FREAD | FWRITE)) == (FREAD | FWRITE))) {
  217                 /* simplex or not, better safe than sorry. */
  218                 pcm_unlock(d);
  219                 error = EBUSY;
  220                 goto out;
  221         }
  222 
  223         /*
  224          * if we get here, the open request is valid- either:
  225          *   * we were previously not open
  226          *   * we were open for play xor record and the opener wants
  227          *     the non-open direction
  228          */
  229         if (flags & FREAD) {
  230                 /* open for read */
  231                 pcm_unlock(d);
  232                 error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, chnum);
  233                 if (error != 0 && error != EBUSY && chnum != -1 && (flags & FWRITE))
  234                         error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, -1);
  235 
  236                 if (error == 0 && (chn_reset(rdch, fmt) ||
  237                                 (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED))))
  238                         error = ENODEV;
  239 
  240                 if (error != 0) {
  241                         if (rdch)
  242                                 pcm_chnrelease(rdch);
  243                         goto out;
  244                 }
  245 
  246                 pcm_chnref(rdch, 1);
  247                 CHN_UNLOCK(rdch);
  248                 pcm_lock(d);
  249         }
  250 
  251         if (flags & FWRITE) {
  252             /* open for write */
  253             pcm_unlock(d);
  254             error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, chnum);
  255             if (error != 0 && error != EBUSY && chnum != -1 && (flags & FREAD))
  256                 error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, -1);
  257 
  258             if (error == 0 && (chn_reset(wrch, fmt) ||
  259                         (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED))))
  260                 error = ENODEV;
  261 
  262             if (error != 0) {
  263                 if (wrch)
  264                     pcm_chnrelease(wrch);
  265                 if (rdch) {
  266                     /*
  267                      * Lock, deref and release previously created record channel
  268                      */
  269                     CHN_LOCK(rdch);
  270                     pcm_chnref(rdch, -1);
  271                     pcm_chnrelease(rdch);
  272                 }
  273 
  274                 goto out;
  275             }
  276 
  277             pcm_chnref(wrch, 1);
  278             CHN_UNLOCK(wrch);
  279             pcm_lock(d);
  280         }
  281 
  282         i_dev->si_drv1 = rdch;
  283         i_dev->si_drv2 = wrch;
  284 
  285         sce->open++;
  286 
  287         pcm_unlock(d);
  288         return 0;
  289 
  290 out:
  291         if (i_dev != NULL && sce != NULL && sce->open == 0) {
  292                 pcm_lock(d);
  293                 destroy_dev(i_dev);
  294                 sce->dsp_dev = NULL;
  295                 pcm_unlock(d);
  296         }
  297         return (error);
  298 }
  299 
  300 static int
  301 dsp_close(struct dev_close_args *ap)
  302 {
  303         struct cdev *i_dev = ap->a_head.a_dev;
  304         struct pcm_channel *rdch, *wrch;
  305         struct snddev_info *d;
  306         struct snddev_channel *sce = NULL;
  307         int refs;
  308 
  309         d = dsp_get_info(i_dev);
  310         pcm_lock(d);
  311         rdch = i_dev->si_drv1;
  312         wrch = i_dev->si_drv2;
  313         i_dev->si_drv1 = NULL;
  314         i_dev->si_drv2 = NULL;
  315 
  316         SLIST_FOREACH(sce, &d->channels, link) {
  317                 if (sce->dsp_dev == i_dev)
  318                         break;
  319         }
  320         sce->dsp_dev = NULL;
  321         destroy_dev(i_dev);
  322 
  323         pcm_unlock(d);
  324 
  325         if (rdch || wrch) {
  326                 refs = 0;
  327                 if (rdch) {
  328                         CHN_LOCK(rdch);
  329                         refs += pcm_chnref(rdch, -1);
  330                         chn_abort(rdch); /* won't sleep */
  331                         rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
  332                         chn_reset(rdch, 0);
  333                         pcm_chnrelease(rdch);
  334                 }
  335                 if (wrch) {
  336                         CHN_LOCK(wrch);
  337                         refs += pcm_chnref(wrch, -1);
  338                         /*
  339                          * XXX: Maybe the right behaviour is to abort on non_block.
  340                          * It seems that mplayer flushes the audio queue by quickly
  341                          * closing and re-opening.  In FBSD, there's a long pause
  342                          * while the audio queue flushes that I presume isn't there in
  343                          * linux.
  344                          */
  345                         chn_flush(wrch); /* may sleep */
  346                         wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
  347                         chn_reset(wrch, 0);
  348                         pcm_chnrelease(wrch);
  349                 }
  350 
  351                 pcm_lock(d);
  352                 /*
  353                  * If there are no more references, release the channels.
  354                  */
  355                 if (refs == 0) {
  356                         if (pcm_getfakechan(d))
  357                                 pcm_getfakechan(d)->flags = 0;
  358                         /* What is this?!? */
  359                         dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
  360                 }
  361                 pcm_unlock(d);
  362         }
  363         return 0;
  364 }
  365 
  366 static int
  367 dsp_read(struct dev_read_args *ap)
  368 {
  369         struct cdev *i_dev = ap->a_head.a_dev;
  370         struct uio *buf = ap->a_uio;
  371         int flag = ap->a_ioflag;
  372         struct pcm_channel *rdch, *wrch;
  373         int ret;
  374 
  375         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
  376 
  377         KASSERT(rdch, ("dsp_read: nonexistant channel"));
  378         KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
  379 
  380         if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
  381                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
  382                 return EINVAL;
  383         }
  384         if (!(rdch->flags & CHN_F_RUNNING))
  385                 rdch->flags |= CHN_F_RUNNING;
  386         ret = chn_read(rdch, buf, flag);
  387         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
  388 
  389         return ret;
  390 }
  391 
  392 static int
  393 dsp_write(struct dev_write_args *ap)
  394 {
  395         struct cdev *i_dev = ap->a_head.a_dev;
  396         struct uio *buf = ap->a_uio;
  397         int flag = ap->a_ioflag;
  398         struct pcm_channel *rdch, *wrch;
  399         int ret;
  400 
  401         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
  402 
  403         KASSERT(wrch, ("dsp_write: nonexistant channel"));
  404         KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
  405 
  406         if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
  407                 relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
  408                 return EINVAL;
  409         }
  410         if (!(wrch->flags & CHN_F_RUNNING))
  411                 wrch->flags |= CHN_F_RUNNING;
  412         ret = chn_write(wrch, buf, flag);
  413         relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
  414 
  415         return ret;
  416 }
  417 
  418 static int
  419 dsp_ioctl(struct dev_ioctl_args *ap)
  420 {
  421         struct cdev *i_dev = ap->a_head.a_dev;
  422         u_long cmd = ap->a_cmd;
  423         caddr_t arg = ap->a_data;
  424         struct pcm_channel *chn, *rdch, *wrch;
  425         struct snddev_info *d;
  426         int kill;
  427         int ret = 0, *arg_i = (int *)arg, tmp;
  428 
  429         d = dsp_get_info(i_dev);
  430         getchns(i_dev, &rdch, &wrch, 0);
  431 
  432         kill = 0;
  433         if (wrch && (wrch->flags & CHN_F_DEAD))
  434                 kill |= 1;
  435         if (rdch && (rdch->flags & CHN_F_DEAD))
  436                 kill |= 2;
  437         if (kill == 3) {
  438                 relchns(i_dev, rdch, wrch, 0);
  439                 return EINVAL;
  440         }
  441         if (kill & 1)
  442                 wrch = NULL;
  443         if (kill & 2)
  444                 rdch = NULL;
  445 
  446         /*
  447          * 4Front OSS specifies that dsp devices allow mixer controls to
  448          * control PCM == their volume.
  449          */
  450         if (IOCGROUP(cmd) == 'M') {
  451                 /*
  452                  * For now only set the channel volume for vchans, pass
  453                  * all others to the mixer.
  454                  */
  455                 if (wrch != NULL && wrch->flags & CHN_F_VIRTUAL &&
  456                     (cmd & 0xff) == SOUND_MIXER_PCM) {
  457                         if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
  458                                 int vol_raw = *(int *)arg;
  459                                 int vol_left, vol_right;
  460 
  461                                 vol_left = min(vol_raw & 0x00ff, 100);
  462                                 vol_right = min((vol_raw & 0xff00) >> 8, 100);
  463                                 ret = chn_setvolume(wrch, vol_left, vol_right);
  464                         } else {
  465                                 *(int *)arg = wrch->volume;
  466                         }
  467                 } else {
  468                         ap->a_head.a_dev = d->mixer_dev;
  469                         ret = mixer_ioctl(ap);
  470                 }
  471 
  472                 relchns(i_dev, rdch, wrch, 0);
  473                 return ret;
  474         }
  475         
  476         switch(cmd) {
  477 #ifdef OLDPCM_IOCTL
  478         /*
  479          * we start with the new ioctl interface.
  480          */
  481         case AIONWRITE: /* how many bytes can write ? */
  482                 if (wrch) {
  483                         CHN_LOCK(wrch);
  484 /*
  485                 if (wrch && wrch->bufhard.dl)
  486                         while (chn_wrfeed(wrch) == 0);
  487 */
  488                         *arg_i = sndbuf_getfree(wrch->bufsoft);
  489                         CHN_UNLOCK(wrch);
  490                 } else {
  491                         *arg_i = 0;
  492                         ret = EINVAL;
  493                 }
  494                 break;
  495 
  496         case AIOSSIZE:     /* set the current blocksize */
  497                 {
  498                         struct snd_size *p = (struct snd_size *)arg;
  499 
  500                         p->play_size = 0;
  501                         p->rec_size = 0;
  502                         if (wrch) {
  503                                 CHN_LOCK(wrch);
  504                                 chn_setblocksize(wrch, 2, p->play_size);
  505                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
  506                                 CHN_UNLOCK(wrch);
  507                         }
  508                         if (rdch) {
  509                                 CHN_LOCK(rdch);
  510                                 chn_setblocksize(rdch, 2, p->rec_size);
  511                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
  512                                 CHN_UNLOCK(rdch);
  513                         }
  514                 }
  515                 break;
  516         case AIOGSIZE:  /* get the current blocksize */
  517                 {
  518                         struct snd_size *p = (struct snd_size *)arg;
  519 
  520                         if (wrch) {
  521                                 CHN_LOCK(wrch);
  522                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
  523                                 CHN_UNLOCK(wrch);
  524                         }
  525                         if (rdch) {
  526                                 CHN_LOCK(rdch);
  527                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
  528                                 CHN_UNLOCK(rdch);
  529                         }
  530                 }
  531                 break;
  532 
  533         case AIOSFMT:
  534         case AIOGFMT:
  535                 {
  536                         snd_chan_param *p = (snd_chan_param *)arg;
  537 
  538                         if (cmd == AIOSFMT &&
  539                             ((p->play_format != 0 && p->play_rate == 0) ||
  540                             (p->rec_format != 0 && p->rec_rate == 0))) {
  541                                 ret = EINVAL;
  542                                 break;
  543                         }
  544                         if (wrch) {
  545                                 CHN_LOCK(wrch);
  546                                 if (cmd == AIOSFMT && p->play_format != 0) {
  547                                         chn_setformat(wrch, p->play_format);
  548                                         chn_setspeed(wrch, p->play_rate);
  549                                 }
  550                                 p->play_rate = wrch->speed;
  551                                 p->play_format = wrch->format;
  552                                 CHN_UNLOCK(wrch);
  553                         } else {
  554                                 p->play_rate = 0;
  555                                 p->play_format = 0;
  556                         }
  557                         if (rdch) {
  558                                 CHN_LOCK(rdch);
  559                                 if (cmd == AIOSFMT && p->rec_format != 0) {
  560                                         chn_setformat(rdch, p->rec_format);
  561                                         chn_setspeed(rdch, p->rec_rate);
  562                                 }
  563                                 p->rec_rate = rdch->speed;
  564                                 p->rec_format = rdch->format;
  565                                 CHN_UNLOCK(rdch);
  566                         } else {
  567                                 p->rec_rate = 0;
  568                                 p->rec_format = 0;
  569                         }
  570                 }
  571                 break;
  572 
  573         case AIOGCAP:     /* get capabilities */
  574                 {
  575                         snd_capabilities *p = (snd_capabilities *)arg;
  576                         struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
  577                         struct cdev *pdev;
  578 
  579                         if (rdch) {
  580                                 CHN_LOCK(rdch);
  581                                 rcaps = chn_getcaps(rdch);
  582                         }
  583                         if (wrch) {
  584                                 CHN_LOCK(wrch);
  585                                 pcaps = chn_getcaps(wrch);
  586                         }
  587                         p->rate_min = max(rcaps? rcaps->minspeed : 0,
  588                                           pcaps? pcaps->minspeed : 0);
  589                         p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
  590                                           pcaps? pcaps->maxspeed : 1000000);
  591                         p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
  592                                          wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
  593                         /* XXX bad on sb16 */
  594                         p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
  595                                      (wrch? chn_getformats(wrch) : 0xffffffff);
  596                         if (rdch && wrch)
  597                                 p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
  598                         pdev = d->mixer_dev;
  599                         p->mixers = 1; /* default: one mixer */
  600                         p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
  601                         p->left = p->right = 100;
  602                         if (rdch)
  603                                 CHN_UNLOCK(rdch);
  604                         if (wrch)
  605                                 CHN_UNLOCK(wrch);
  606                 }
  607                 break;
  608 
  609         case AIOSTOP:
  610                 if (*arg_i == AIOSYNC_PLAY && wrch) {
  611                         CHN_LOCK(wrch);
  612                         *arg_i = chn_abort(wrch);
  613                         CHN_UNLOCK(wrch);
  614                 } else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
  615                         CHN_LOCK(rdch);
  616                         *arg_i = chn_abort(rdch);
  617                         CHN_UNLOCK(rdch);
  618                 } else {
  619                         kprintf("AIOSTOP: bad channel 0x%x\n", *arg_i);
  620                         *arg_i = 0;
  621                 }
  622                 break;
  623 
  624         case AIOSYNC:
  625                 kprintf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
  626                         ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
  627                 break;
  628 #endif
  629         /*
  630          * here follow the standard ioctls (filio.h etc.)
  631          */
  632         case FIONREAD: /* get # bytes to read */
  633                 if (rdch) {
  634                         CHN_LOCK(rdch);
  635 /*                      if (rdch && rdch->bufhard.dl)
  636                                 while (chn_rdfeed(rdch) == 0);
  637 */
  638                         *arg_i = sndbuf_getready(rdch->bufsoft);
  639                         CHN_UNLOCK(rdch);
  640                 } else {
  641                         *arg_i = 0;
  642                         ret = EINVAL;
  643                 }
  644                 break;
  645 
  646         case FIOASYNC: /*set/clear async i/o */
  647                 DEB( kprintf("FIOASYNC\n") ; )
  648                 break;
  649 
  650         case SNDCTL_DSP_NONBLOCK:
  651         case FIONBIO: /* set/clear non-blocking i/o */
  652                 if (rdch) {
  653                         CHN_LOCK(rdch);
  654                         if (*arg_i)
  655                                 rdch->flags |= CHN_F_NBIO;
  656                         else
  657                                 rdch->flags &= ~CHN_F_NBIO;
  658                         CHN_UNLOCK(rdch);
  659                 }
  660                 if (wrch) {
  661                         CHN_LOCK(wrch);
  662                         if (*arg_i)
  663                                 wrch->flags |= CHN_F_NBIO;
  664                         else
  665                                 wrch->flags &= ~CHN_F_NBIO;
  666                         CHN_UNLOCK(wrch);
  667                 }
  668                 break;
  669 
  670         /*
  671          * Finally, here is the linux-compatible ioctl interface
  672          */
  673 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
  674         case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
  675         case SNDCTL_DSP_GETBLKSIZE:
  676                 chn = wrch ? wrch : rdch;
  677                 if (chn) {
  678                         CHN_LOCK(chn);
  679                         *arg_i = sndbuf_getblksz(chn->bufsoft);
  680                         CHN_UNLOCK(chn);
  681                 } else {
  682                         *arg_i = 0;
  683                         ret = EINVAL;
  684                 }
  685                 break ;
  686 
  687         case SNDCTL_DSP_SETBLKSIZE:
  688                 RANGE(*arg_i, 16, 65536);
  689                 if (wrch) {
  690                         CHN_LOCK(wrch);
  691                         chn_setblocksize(wrch, 2, *arg_i);
  692                         CHN_UNLOCK(wrch);
  693                 }
  694                 if (rdch) {
  695                         CHN_LOCK(rdch);
  696                         chn_setblocksize(rdch, 2, *arg_i);
  697                         CHN_UNLOCK(rdch);
  698                 }
  699                 break;
  700 
  701         case SNDCTL_DSP_RESET:
  702                 DEB(kprintf("dsp reset\n"));
  703                 if (wrch) {
  704                         CHN_LOCK(wrch);
  705                         chn_abort(wrch);
  706                         chn_resetbuf(wrch);
  707                         CHN_UNLOCK(wrch);
  708                 }
  709                 if (rdch) {
  710                         CHN_LOCK(rdch);
  711                         chn_abort(rdch);
  712                         chn_resetbuf(rdch);
  713                         CHN_UNLOCK(rdch);
  714                 }
  715                 break;
  716 
  717         case SNDCTL_DSP_SYNC:
  718                 DEB(kprintf("dsp sync\n"));
  719                 /* chn_sync may sleep */
  720                 if (wrch) {
  721                         CHN_LOCK(wrch);
  722                         chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
  723                         CHN_UNLOCK(wrch);
  724                 }
  725                 break;
  726 
  727         case SNDCTL_DSP_SPEED:
  728                 /* chn_setspeed may sleep */
  729                 tmp = 0;
  730                 if (wrch) {
  731                         CHN_LOCK(wrch);
  732                         ret = chn_setspeed(wrch, *arg_i);
  733                         tmp = wrch->speed;
  734                         CHN_UNLOCK(wrch);
  735                 }
  736                 if (rdch && ret == 0) {
  737                         CHN_LOCK(rdch);
  738                         ret = chn_setspeed(rdch, *arg_i);
  739                         if (tmp == 0)
  740                                 tmp = rdch->speed;
  741                         CHN_UNLOCK(rdch);
  742                 }
  743                 *arg_i = tmp;
  744                 break;
  745 
  746         case SOUND_PCM_READ_RATE:
  747                 chn = wrch ? wrch : rdch;
  748                 if (chn) {
  749                         CHN_LOCK(chn);
  750                         *arg_i = chn->speed;
  751                         CHN_UNLOCK(chn);
  752                 } else {
  753                         *arg_i = 0;
  754                         ret = EINVAL;
  755                 }
  756                 break;
  757 
  758         case SNDCTL_DSP_STEREO:
  759                 tmp = -1;
  760                 *arg_i = (*arg_i)? AFMT_STEREO : 0;
  761                 if (wrch) {
  762                         CHN_LOCK(wrch);
  763                         ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
  764                         tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
  765                         CHN_UNLOCK(wrch);
  766                 }
  767                 if (rdch && ret == 0) {
  768                         CHN_LOCK(rdch);
  769                         ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
  770                         if (tmp == -1)
  771                                 tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
  772                         CHN_UNLOCK(rdch);
  773                 }
  774                 *arg_i = tmp;
  775                 break;
  776 
  777         case SOUND_PCM_WRITE_CHANNELS:
  778 /*      case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
  779                 if (*arg_i != 0) {
  780                         tmp = 0;
  781                         *arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
  782                         if (wrch) {
  783                                 CHN_LOCK(wrch);
  784                                 ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
  785                                 tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
  786                                 CHN_UNLOCK(wrch);
  787                         }
  788                         if (rdch && ret == 0) {
  789                                 CHN_LOCK(rdch);
  790                                 ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
  791                                 if (tmp == 0)
  792                                         tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
  793                                 CHN_UNLOCK(rdch);
  794                         }
  795                         *arg_i = tmp;
  796                 } else {
  797                         chn = wrch ? wrch : rdch;
  798                         CHN_LOCK(chn);
  799                         *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
  800                         CHN_UNLOCK(chn);
  801                 }
  802                 break;
  803 
  804         case SOUND_PCM_READ_CHANNELS:
  805                 chn = wrch ? wrch : rdch;
  806                 if (chn) {
  807                         CHN_LOCK(chn);
  808                         *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
  809                         CHN_UNLOCK(chn);
  810                 } else {
  811                         *arg_i = 0;
  812                         ret = EINVAL;
  813                 }
  814                 break;
  815 
  816         case SNDCTL_DSP_GETFMTS:        /* returns a mask of supported fmts */
  817                 chn = wrch ? wrch : rdch;
  818                 if (chn) {
  819                         CHN_LOCK(chn);
  820                         *arg_i = chn_getformats(chn);
  821                         CHN_UNLOCK(chn);
  822                 } else {
  823                         *arg_i = 0;
  824                         ret = EINVAL;
  825                 }
  826                 break ;
  827 
  828         case SNDCTL_DSP_SETFMT: /* sets _one_ format */
  829                 if ((*arg_i != AFMT_QUERY)) {
  830                         tmp = 0;
  831                         if (wrch) {
  832                                 CHN_LOCK(wrch);
  833                                 ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
  834                                 tmp = wrch->format & ~AFMT_STEREO;
  835                                 CHN_UNLOCK(wrch);
  836                         }
  837                         if (rdch && ret == 0) {
  838                                 CHN_LOCK(rdch);
  839                                 ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
  840                                 if (tmp == 0)
  841                                         tmp = rdch->format & ~AFMT_STEREO;
  842                                 CHN_UNLOCK(rdch);
  843                         }
  844                         *arg_i = tmp;
  845                 } else {
  846                         chn = wrch ? wrch : rdch;
  847                         CHN_LOCK(chn);
  848                         *arg_i = chn->format & ~AFMT_STEREO;
  849                         CHN_UNLOCK(chn);
  850                 }
  851                 break;
  852 
  853         case SNDCTL_DSP_SETFRAGMENT:
  854                 DEB(kprintf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
  855                 {
  856                         u_int32_t fragln = (*arg_i) & 0x0000ffff;
  857                         u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
  858                         u_int32_t fragsz;
  859                         u_int32_t r_maxfrags, r_fragsz;
  860 
  861                         RANGE(fragln, 4, 16);
  862                         fragsz = 1 << fragln;
  863 
  864                         if (maxfrags == 0)
  865                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
  866                         if (maxfrags < 2)
  867                                 maxfrags = 2;
  868                         if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
  869                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
  870 
  871                         DEB(kprintf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
  872                         if (rdch) {
  873                                 CHN_LOCK(rdch);
  874                                 ret = chn_setblocksize(rdch, maxfrags, fragsz);
  875                                 r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
  876                                 r_fragsz = sndbuf_getblksz(rdch->bufsoft);
  877                                 CHN_UNLOCK(rdch);
  878                         } else {
  879                                 r_maxfrags = maxfrags;
  880                                 r_fragsz = fragsz;
  881                         }
  882                         if (wrch && ret == 0) {
  883                                 CHN_LOCK(wrch);
  884                                 ret = chn_setblocksize(wrch, maxfrags, fragsz);
  885                                 maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
  886                                 fragsz = sndbuf_getblksz(wrch->bufsoft);
  887                                 CHN_UNLOCK(wrch);
  888                         } else { /* use whatever came from the read channel */
  889                                 maxfrags = r_maxfrags;
  890                                 fragsz = r_fragsz;
  891                         }
  892 
  893                         fragln = 0;
  894                         while (fragsz > 1) {
  895                                 fragln++;
  896                                 fragsz >>= 1;
  897                         }
  898                         *arg_i = (maxfrags << 16) | fragln;
  899                 }
  900                 break;
  901 
  902         case SNDCTL_DSP_GETISPACE:
  903                 /* return the size of data available in the input queue */
  904                 {
  905                         audio_buf_info *a = (audio_buf_info *)arg;
  906                         if (rdch) {
  907                                 struct snd_dbuf *bs = rdch->bufsoft;
  908 
  909                                 CHN_LOCK(rdch);
  910                                 a->bytes = sndbuf_getready(bs);
  911                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
  912                                 a->fragstotal = sndbuf_getblkcnt(bs);
  913                                 a->fragsize = sndbuf_getblksz(bs);
  914                                 CHN_UNLOCK(rdch);
  915                         }
  916                 }
  917                 break;
  918 
  919         case SNDCTL_DSP_GETOSPACE:
  920                 /* return space available in the output queue */
  921                 {
  922                         audio_buf_info *a = (audio_buf_info *)arg;
  923                         if (wrch) {
  924                                 struct snd_dbuf *bs = wrch->bufsoft;
  925 
  926                                 CHN_LOCK(wrch);
  927                                 /* XXX abusive DMA update: chn_wrupdate(wrch); */
  928                                 a->bytes = sndbuf_getfree(bs);
  929                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
  930                                 a->fragstotal = sndbuf_getblkcnt(bs);
  931                                 a->fragsize = sndbuf_getblksz(bs);
  932                                 CHN_UNLOCK(wrch);
  933                         }
  934                 }
  935                 break;
  936 
  937         case SNDCTL_DSP_GETIPTR:
  938                 {
  939                         count_info *a = (count_info *)arg;
  940                         if (rdch) {
  941                                 struct snd_dbuf *bs = rdch->bufsoft;
  942 
  943                                 CHN_LOCK(rdch);
  944                                 /* XXX abusive DMA update: chn_rdupdate(rdch); */
  945                                 a->bytes = sndbuf_gettotal(bs);
  946                                 a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
  947                                 a->ptr = sndbuf_getreadyptr(bs);
  948                                 rdch->blocks = sndbuf_getblocks(bs);
  949                                 CHN_UNLOCK(rdch);
  950                         } else
  951                                 ret = EINVAL;
  952                 }
  953                 break;
  954 
  955         case SNDCTL_DSP_GETOPTR:
  956                 {
  957                         count_info *a = (count_info *)arg;
  958                         if (wrch) {
  959                                 struct snd_dbuf *bs = wrch->bufsoft;
  960 
  961                                 CHN_LOCK(wrch);
  962                                 /* XXX abusive DMA update: chn_wrupdate(wrch); */
  963                                 a->bytes = sndbuf_gettotal(bs);
  964                                 a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
  965                                 a->ptr = sndbuf_getreadyptr(bs);
  966                                 wrch->blocks = sndbuf_getblocks(bs);
  967                                 CHN_UNLOCK(wrch);
  968                         } else
  969                                 ret = EINVAL;
  970                 }
  971                 break;
  972 
  973         case SNDCTL_DSP_GETCAPS:
  974                 *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
  975                 if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
  976                         *arg_i |= DSP_CAP_DUPLEX;
  977                 break;
  978 
  979         case SOUND_PCM_READ_BITS:
  980                 chn = wrch ? wrch : rdch;
  981                 if (chn) {
  982                         CHN_LOCK(chn);
  983                         if (chn->format & AFMT_8BIT)
  984                                 *arg_i = 8;
  985                         else if (chn->format & AFMT_16BIT)
  986                                 *arg_i = 16;
  987                         else if (chn->format & AFMT_24BIT)
  988                                 *arg_i = 24;
  989                         else if (chn->format & AFMT_32BIT)
  990                                 *arg_i = 32;
  991                         else
  992                                 ret = EINVAL;
  993                         CHN_UNLOCK(chn);
  994                 } else {
  995                         *arg_i = 0;
  996                         ret = EINVAL;
  997                 }
  998                 break;
  999 
 1000         case SNDCTL_DSP_SETTRIGGER:
 1001                 if (rdch) {
 1002                         CHN_LOCK(rdch);
 1003                         rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
 1004                         if (*arg_i & PCM_ENABLE_INPUT)
 1005                                 chn_start(rdch, 1);
 1006                         else
 1007                                 rdch->flags |= CHN_F_NOTRIGGER;
 1008                         CHN_UNLOCK(rdch);
 1009                 }
 1010                 if (wrch) {
 1011                         CHN_LOCK(wrch);
 1012                         wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
 1013                         if (*arg_i & PCM_ENABLE_OUTPUT)
 1014                                 chn_start(wrch, 1);
 1015                         else
 1016                                 wrch->flags |= CHN_F_NOTRIGGER;
 1017                         CHN_UNLOCK(wrch);
 1018                 }
 1019                 break;
 1020 
 1021         case SNDCTL_DSP_GETTRIGGER:
 1022                 *arg_i = 0;
 1023                 if (wrch) {
 1024                         CHN_LOCK(wrch);
 1025                         if (wrch->flags & CHN_F_TRIGGERED)
 1026                                 *arg_i |= PCM_ENABLE_OUTPUT;
 1027                         CHN_UNLOCK(wrch);
 1028                 }
 1029                 if (rdch) {
 1030                         CHN_LOCK(rdch);
 1031                         if (rdch->flags & CHN_F_TRIGGERED)
 1032                                 *arg_i |= PCM_ENABLE_INPUT;
 1033                         CHN_UNLOCK(rdch);
 1034                 }
 1035                 break;
 1036 
 1037         case SNDCTL_DSP_GETODELAY:
 1038                 if (wrch) {
 1039                         struct snd_dbuf *b = wrch->bufhard;
 1040                         struct snd_dbuf *bs = wrch->bufsoft;
 1041 
 1042                         CHN_LOCK(wrch);
 1043                         /* XXX abusive DMA update: chn_wrupdate(wrch); */
 1044                         *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
 1045                         CHN_UNLOCK(wrch);
 1046                 } else
 1047                         ret = EINVAL;
 1048                 break;
 1049 
 1050         case SNDCTL_DSP_POST:
 1051                 if (wrch) {
 1052                         CHN_LOCK(wrch);
 1053                         wrch->flags &= ~CHN_F_NOTRIGGER;
 1054                         chn_start(wrch, 1);
 1055                         CHN_UNLOCK(wrch);
 1056                 }
 1057                 break;
 1058 
 1059         case SNDCTL_DSP_SETDUPLEX:
 1060                 /*
 1061                  * switch to full-duplex mode if card is in half-duplex
 1062                  * mode and is able to work in full-duplex mode
 1063                  */
 1064                 if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
 1065                         dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
 1066                 break;
 1067 
 1068         case SNDCTL_DSP_MAPINBUF:
 1069         case SNDCTL_DSP_MAPOUTBUF:
 1070         case SNDCTL_DSP_SETSYNCRO:
 1071                 /* undocumented */
 1072 
 1073         case SNDCTL_DSP_SUBDIVIDE:
 1074         case SOUND_PCM_WRITE_FILTER:
 1075         case SOUND_PCM_READ_FILTER:
 1076                 /* dunno what these do, don't sound important */
 1077 
 1078         default:
 1079                 DEB(kprintf("default ioctl fn 0x%08lx fail\n", cmd));
 1080                 ret = EINVAL;
 1081                 break;
 1082         }
 1083         relchns(i_dev, rdch, wrch, 0);
 1084         return ret;
 1085 }
 1086 
 1087 static struct filterops dsp_read_filtops =
 1088         { FILTEROP_ISFD, NULL, dsp_filter_detach, dsp_filter_read };
 1089 static struct filterops dsp_write_filtops =
 1090         { FILTEROP_ISFD, NULL, dsp_filter_detach, dsp_filter_write };
 1091 
 1092 static int
 1093 dsp_kqfilter(struct dev_kqfilter_args *ap)
 1094 {
 1095         struct knote *kn = ap->a_kn;
 1096         struct klist *klist;
 1097         struct cdev *i_dev = ap->a_head.a_dev;
 1098         struct pcm_channel *wrch = NULL, *rdch = NULL;
 1099         struct snd_dbuf *bs = NULL;
 1100 
 1101         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1102 
 1103         switch (kn->kn_filter) {
 1104         case EVFILT_READ:
 1105                 if (rdch) {
 1106                         kn->kn_fop = &dsp_read_filtops;
 1107                         kn->kn_hook = (caddr_t)rdch;
 1108                         bs = rdch->bufsoft;
 1109                         ap->a_result = 0;
 1110                 }
 1111                 break;
 1112         case EVFILT_WRITE:
 1113                 if (wrch) {
 1114                         kn->kn_fop = &dsp_write_filtops;
 1115                         kn->kn_hook = (caddr_t)wrch;
 1116                         bs = wrch->bufsoft;
 1117                         ap->a_result = 0;
 1118                 }
 1119                 break;
 1120         default:
 1121                 ap->a_result = EOPNOTSUPP;
 1122                 break;
 1123         }
 1124 
 1125         if (ap->a_result == 0) {
 1126                 klist = &sndbuf_getkq(bs)->ki_note;
 1127                 knote_insert(klist, kn);
 1128         }
 1129 
 1130         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1131 
 1132         return (0);
 1133 }
 1134 
 1135 static void
 1136 dsp_filter_detach(struct knote *kn)
 1137 {
 1138         struct pcm_channel *ch = (struct pcm_channel *)kn->kn_hook;
 1139         struct snd_dbuf *bs = ch->bufsoft;
 1140         struct klist *klist;
 1141 
 1142         CHN_LOCK(ch);
 1143         klist = &sndbuf_getkq(bs)->ki_note;
 1144         knote_remove(klist, kn);
 1145         CHN_UNLOCK(ch);
 1146 }
 1147 
 1148 static int
 1149 dsp_filter_read(struct knote *kn, long hint)
 1150 {
 1151         struct pcm_channel *rdch = (struct pcm_channel *)kn->kn_hook;
 1152         struct thread *td = curthread;
 1153         int ready;
 1154 
 1155         CHN_LOCK(rdch);
 1156         ready = chn_poll(rdch, 1, td);
 1157         CHN_UNLOCK(rdch);
 1158 
 1159         return (ready);
 1160 }
 1161 
 1162 static int
 1163 dsp_filter_write(struct knote *kn, long hint)
 1164 {
 1165         struct pcm_channel *wrch = (struct pcm_channel *)kn->kn_hook;
 1166         struct thread *td = curthread;
 1167         int ready;
 1168 
 1169         CHN_LOCK(wrch);
 1170         ready = chn_poll(wrch, 1, td);
 1171         CHN_UNLOCK(wrch);
 1172 
 1173         return (ready);
 1174 }
 1175 
 1176 static int
 1177 dsp_mmap(struct dev_mmap_args *ap)
 1178 {
 1179         struct cdev *i_dev = ap->a_head.a_dev;
 1180         vm_offset_t offset = ap->a_offset;
 1181         int nprot = ap->a_nprot;
 1182         struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
 1183 
 1184         if (nprot & PROT_EXEC)
 1185                 return -1;
 1186 
 1187         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1188 #if 0
 1189         /*
 1190          * XXX the linux api uses the nprot to select read/write buffer
 1191          * our vm system doesn't allow this, so force write buffer
 1192          */
 1193 
 1194         if (wrch && (nprot & PROT_WRITE)) {
 1195                 c = wrch;
 1196         } else if (rdch && (nprot & PROT_READ)) {
 1197                 c = rdch;
 1198         } else {
 1199                 return -1;
 1200         }
 1201 #else
 1202         c = wrch;
 1203 #endif
 1204 
 1205         if (c == NULL) {
 1206                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1207                 return -1;
 1208         }
 1209 
 1210         if (offset >= sndbuf_getsize(c->bufsoft)) {
 1211                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1212                 return -1;
 1213         }
 1214 
 1215         if (!(c->flags & CHN_F_MAPPED))
 1216                 c->flags |= CHN_F_MAPPED;
 1217 
 1218         ap->a_result = atop(vtophys(sndbuf_getbufofs(c->bufsoft, offset)));
 1219         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1220 
 1221         return (0);
 1222 }
 1223 
 1224 /*
 1225  *    for i = 0 to channels of device N
 1226  *      if dspN.i isn't busy and in the right dir, create a dev_t and return it
 1227  */
 1228 int
 1229 dsp_clone(struct dev_clone_args *ap)
 1230 {
 1231         struct cdev *i_dev = ap->a_head.a_dev;
 1232         struct cdev *pdev;
 1233         struct snddev_info *pcm_dev;
 1234         struct snddev_channel *pcm_chan;
 1235         struct pcm_channel *c;
 1236         int err = EBUSY;
 1237         int dir;
 1238 
 1239         pcm_dev = dsp_get_info(i_dev);
 1240 
 1241         if (pcm_dev == NULL)
 1242                 return (ENODEV);
 1243 
 1244         dir = ap->a_mode & FWRITE ? PCMDIR_PLAY : PCMDIR_REC;
 1245 
 1246 retry_chnalloc:
 1247         SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) {
 1248                 c = pcm_chan->channel;
 1249                 CHN_LOCK(c);
 1250                 pdev = pcm_chan->dsp_dev;
 1251 
 1252                 /*
 1253                  * Make sure that the channel has not been assigned
 1254                  * to a device yet (and vice versa).
 1255                  * The direction has to match and the channel may not
 1256                  * be busy.
 1257                  * dsp_open will use exactly this channel number to
 1258                  * avoid (possible?) races between clone and open.
 1259                  */
 1260                 if (pdev == NULL && c->direction == dir &&
 1261                     !(c->flags & CHN_F_BUSY)) {
 1262                         CHN_UNLOCK(c);
 1263                         pcm_lock(pcm_dev);
 1264                         pcm_chan->dsp_dev = make_only_dev(&dsp_ops,
 1265                                 PCMMKMINOR(PCMUNIT(i_dev), pcm_chan->chan_num),
 1266                                 UID_ROOT, GID_WHEEL,
 1267                                 0666,
 1268                                 "%s.%d",
 1269                                 devtoname(i_dev),
 1270                                 pcm_chan->chan_num);
 1271                         pcm_unlock(pcm_dev);
 1272 
 1273                         ap->a_dev = pcm_chan->dsp_dev;
 1274                         return (0);
 1275                 }
 1276                 CHN_UNLOCK(c);
 1277 
 1278 #if DEBUG
 1279                 if ((pdev != NULL) && (pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
 1280                         kprintf("%s: dangling device\n", devtoname(pdev));
 1281                 }
 1282 #endif
 1283         }
 1284 
 1285         /* no channel available, create vchannel */
 1286         if (dir == PCMDIR_PLAY &&
 1287             pcm_dev->vchancount > 0 &&
 1288             pcm_dev->vchancount < snd_maxautovchans &&
 1289             pcm_dev->devcount < PCMMAXCHAN) {
 1290                 err = pcm_setvchans(pcm_dev, pcm_dev->vchancount + 1);
 1291                 if (err == 0)
 1292                         goto retry_chnalloc;
 1293                 /*
 1294                  * If we can't use vchans, because the main output is
 1295                  * blocked for something else, we should not return
 1296                  * any vchan create error, but the more descriptive
 1297                  * EBUSY.
 1298                  * After all, the user didn't ask us to clone, but
 1299                  * only opened /dev/dsp.
 1300                  */
 1301                 err = EBUSY;
 1302         }
 1303 
 1304         return (err);
 1305 }

Cache object: 150bcf649ba7f65e22ff32c99995b23c


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