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-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
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 
   27 #include <sys/param.h>
   28 #include <sys/queue.h>
   29 
   30 #include <dev/sound/pcm/sound.h>
   31 
   32 SND_DECLARE_FILE("$FreeBSD: releng/6.3/sys/dev/sound/pcm/dsp.c 171306 2007-07-08 14:01:11Z ariff $");
   33 
   34 static int dsp_mmap_allow_prot_exec = 0;
   35 SYSCTL_INT(_hw_snd, OID_AUTO, compat_linux_mmap, CTLFLAG_RW,
   36     &dsp_mmap_allow_prot_exec, 0, "linux mmap compatibility");
   37 
   38 #define OLDPCM_IOCTL
   39 
   40 static d_open_t dsp_open;
   41 static d_close_t dsp_close;
   42 static d_read_t dsp_read;
   43 static d_write_t dsp_write;
   44 static d_ioctl_t dsp_ioctl;
   45 static d_poll_t dsp_poll;
   46 static d_mmap_t dsp_mmap;
   47 
   48 struct cdevsw dsp_cdevsw = {
   49         .d_version =    D_VERSION,
   50         .d_flags =      D_NEEDGIANT,
   51         .d_open =       dsp_open,
   52         .d_close =      dsp_close,
   53         .d_read =       dsp_read,
   54         .d_write =      dsp_write,
   55         .d_ioctl =      dsp_ioctl,
   56         .d_poll =       dsp_poll,
   57         .d_mmap =       dsp_mmap,
   58         .d_name =       "dsp",
   59 };
   60 
   61 #ifdef USING_DEVFS
   62 static eventhandler_tag dsp_ehtag;
   63 #endif
   64 
   65 static struct snddev_info *
   66 dsp_get_info(struct cdev *dev)
   67 {
   68         struct snddev_info *d;
   69         int unit;
   70 
   71         unit = PCMUNIT(dev);
   72         if (unit >= devclass_get_maxunit(pcm_devclass))
   73                 return NULL;
   74         d = devclass_get_softc(pcm_devclass, unit);
   75 
   76         return d;
   77 }
   78 
   79 static u_int32_t
   80 dsp_get_flags(struct cdev *dev)
   81 {
   82         device_t bdev;
   83         int unit;
   84 
   85         unit = PCMUNIT(dev);
   86         if (unit >= devclass_get_maxunit(pcm_devclass))
   87                 return 0xffffffff;
   88         bdev = devclass_get_device(pcm_devclass, unit);
   89 
   90         return pcm_getflags(bdev);
   91 }
   92 
   93 static void
   94 dsp_set_flags(struct cdev *dev, u_int32_t flags)
   95 {
   96         device_t bdev;
   97         int unit;
   98 
   99         unit = PCMUNIT(dev);
  100         if (unit >= devclass_get_maxunit(pcm_devclass))
  101                 return;
  102         bdev = devclass_get_device(pcm_devclass, unit);
  103 
  104         pcm_setflags(bdev, flags);
  105 }
  106 
  107 /*
  108  * return the channels associated with an open device instance.
  109  * set the priority if the device is simplex and one direction (only) is
  110  * specified.
  111  * lock channels specified.
  112  */
  113 static int
  114 getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
  115 {
  116         struct snddev_info *d;
  117         u_int32_t flags;
  118 
  119         flags = dsp_get_flags(dev);
  120         d = dsp_get_info(dev);
  121         pcm_inprog(d, 1);
  122         pcm_lock(d);
  123         KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
  124                 ("getchns: read and write both prioritised"));
  125 
  126         if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
  127                 flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
  128                 dsp_set_flags(dev, flags);
  129         }
  130 
  131         *rdch = dev->si_drv1;
  132         *wrch = dev->si_drv2;
  133         if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
  134                 if (prio) {
  135                         if (*rdch && flags & SD_F_PRIO_WR) {
  136                                 dev->si_drv1 = NULL;
  137                                 *rdch = pcm_getfakechan(d);
  138                         } else if (*wrch && flags & SD_F_PRIO_RD) {
  139                                 dev->si_drv2 = NULL;
  140                                 *wrch = pcm_getfakechan(d);
  141                         }
  142                 }
  143 
  144                 pcm_getfakechan(d)->flags |= CHN_F_BUSY;
  145         }
  146         pcm_unlock(d);
  147 
  148         if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
  149                 CHN_LOCK(*rdch);
  150         if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
  151                 CHN_LOCK(*wrch);
  152 
  153         return 0;
  154 }
  155 
  156 /* unlock specified channels */
  157 static void
  158 relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
  159 {
  160         struct snddev_info *d;
  161 
  162         d = dsp_get_info(dev);
  163         if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
  164                 CHN_UNLOCK(wrch);
  165         if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
  166                 CHN_UNLOCK(rdch);
  167         pcm_inprog(d, -1);
  168 }
  169 
  170 static int
  171 dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
  172 {
  173         struct pcm_channel *rdch, *wrch;
  174         struct snddev_info *d;
  175         u_int32_t fmt;
  176         int devtype;
  177         int error;
  178         int chnum;
  179 
  180         if (i_dev == NULL || td == NULL)
  181                 return ENODEV;
  182 
  183         if ((flags & (FREAD | FWRITE)) == 0)
  184                 return EINVAL;
  185 
  186         d = dsp_get_info(i_dev);
  187         devtype = PCMDEV(i_dev);
  188         chnum = -1;
  189 
  190         /* decide default format */
  191         switch (devtype) {
  192         case SND_DEV_DSP16:
  193                 fmt = AFMT_S16_LE;
  194                 break;
  195 
  196         case SND_DEV_DSP:
  197                 fmt = AFMT_U8;
  198                 break;
  199 
  200         case SND_DEV_AUDIO:
  201                 fmt = AFMT_MU_LAW;
  202                 break;
  203 
  204         case SND_DEV_NORESET:
  205                 fmt = 0;
  206                 break;
  207 
  208         case SND_DEV_DSPREC:
  209                 fmt = AFMT_U8;
  210                 if (flags & FWRITE)
  211                         return EINVAL;
  212                 chnum = PCMCHAN(i_dev);
  213                 break;
  214 
  215         default:
  216                 panic("impossible devtype %d", devtype);
  217         }
  218 
  219         /* lock snddev so nobody else can monkey with it */
  220         pcm_lock(d);
  221 
  222         rdch = i_dev->si_drv1;
  223         wrch = i_dev->si_drv2;
  224 
  225         if (rdch || wrch || ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) &&
  226                     (flags & (FREAD | FWRITE)) == (FREAD | FWRITE))) {
  227                 /* simplex or not, better safe than sorry. */
  228                 pcm_unlock(d);
  229                 return EBUSY;
  230         }
  231 
  232         /*
  233          * if we get here, the open request is valid- either:
  234          *   * we were previously not open
  235          *   * we were open for play xor record and the opener wants
  236          *     the non-open direction
  237          */
  238         if (flags & FREAD) {
  239                 /* open for read */
  240                 pcm_unlock(d);
  241                 error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, chnum);
  242                 if (error != 0 && error != EBUSY && chnum != -1 && (flags & FWRITE))
  243                         error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, -1);
  244 
  245                 if (error == 0 && (chn_reset(rdch, fmt) ||
  246                                 (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED))))
  247                         error = ENODEV;
  248 
  249                 if (error != 0) {
  250                         if (rdch)
  251                                 pcm_chnrelease(rdch);
  252                         return error;
  253                 }
  254 
  255                 if (flags & O_NONBLOCK)
  256                         rdch->flags |= CHN_F_NBIO;
  257                 pcm_chnref(rdch, 1);
  258                 CHN_UNLOCK(rdch);
  259                 pcm_lock(d);
  260         }
  261 
  262         if (flags & FWRITE) {
  263             /* open for write */
  264             pcm_unlock(d);
  265             error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, chnum);
  266             if (error != 0 && error != EBUSY && chnum != -1 && (flags & FREAD))
  267                 error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, -1);
  268 
  269             if (error == 0 && (chn_reset(wrch, fmt) ||
  270                         (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED))))
  271                 error = ENODEV;
  272 
  273             if (error != 0) {
  274                 if (wrch)
  275                     pcm_chnrelease(wrch);
  276                 if (rdch) {
  277                     /*
  278                      * Lock, deref and release previously created record channel
  279                      */
  280                     CHN_LOCK(rdch);
  281                     pcm_chnref(rdch, -1);
  282                     pcm_chnrelease(rdch);
  283                 }
  284 
  285                 return error;
  286             }
  287 
  288             if (flags & O_NONBLOCK)
  289                 wrch->flags |= CHN_F_NBIO;
  290             pcm_chnref(wrch, 1);
  291             CHN_UNLOCK(wrch);
  292             pcm_lock(d);
  293         }
  294 
  295         i_dev->si_drv1 = rdch;
  296         i_dev->si_drv2 = wrch;
  297 
  298         pcm_unlock(d);
  299         return 0;
  300 }
  301 
  302 static int
  303 dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
  304 {
  305         struct pcm_channel *rdch, *wrch;
  306         struct snddev_info *d;
  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         pcm_unlock(d);
  314 
  315         if (rdch || wrch) {
  316                 refs = 0;
  317                 if (rdch) {
  318                         CHN_LOCK(rdch);
  319                         refs += pcm_chnref(rdch, -1);
  320                         chn_abort(rdch); /* won't sleep */
  321                         rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
  322                         chn_reset(rdch, 0);
  323                         pcm_chnrelease(rdch);
  324                 }
  325                 if (wrch) {
  326                         CHN_LOCK(wrch);
  327                         refs += pcm_chnref(wrch, -1);
  328                         /*
  329                          * XXX: Maybe the right behaviour is to abort on non_block.
  330                          * It seems that mplayer flushes the audio queue by quickly
  331                          * closing and re-opening.  In FBSD, there's a long pause
  332                          * while the audio queue flushes that I presume isn't there in
  333                          * linux.
  334                          */
  335                         chn_flush(wrch); /* may sleep */
  336                         wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
  337                         chn_reset(wrch, 0);
  338                         pcm_chnrelease(wrch);
  339                 }
  340 
  341                 pcm_lock(d);
  342                 if (rdch)
  343                         i_dev->si_drv1 = NULL;
  344                 if (wrch)
  345                         i_dev->si_drv2 = NULL;
  346                 /*
  347                  * If there are no more references, release the channels.
  348                  */
  349                 if (refs == 0 && i_dev->si_drv1 == NULL &&
  350                             i_dev->si_drv2 == NULL) {
  351                         if (pcm_getfakechan(d))
  352                                 pcm_getfakechan(d)->flags = 0;
  353                         /* What is this?!? */
  354                         dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
  355                 }
  356                 pcm_unlock(d);
  357         }
  358         return 0;
  359 }
  360 
  361 static int
  362 dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
  363 {
  364         struct pcm_channel *rdch, *wrch;
  365         int ret;
  366 
  367         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
  368 
  369         KASSERT(rdch, ("dsp_read: nonexistant channel"));
  370         KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
  371 
  372         if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
  373                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
  374                 return EINVAL;
  375         }
  376         if (!(rdch->flags & CHN_F_RUNNING))
  377                 rdch->flags |= CHN_F_RUNNING;
  378         ret = chn_read(rdch, buf);
  379         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
  380 
  381         return ret;
  382 }
  383 
  384 static int
  385 dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
  386 {
  387         struct pcm_channel *rdch, *wrch;
  388         int ret;
  389 
  390         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
  391 
  392         KASSERT(wrch, ("dsp_write: nonexistant channel"));
  393         KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
  394 
  395         if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
  396                 relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
  397                 return EINVAL;
  398         }
  399         if (!(wrch->flags & CHN_F_RUNNING))
  400                 wrch->flags |= CHN_F_RUNNING;
  401         ret = chn_write(wrch, buf);
  402         relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
  403 
  404         return ret;
  405 }
  406 
  407 static int
  408 dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
  409 {
  410         struct pcm_channel *chn, *rdch, *wrch;
  411         struct snddev_info *d;
  412         int kill;
  413         int ret = 0, *arg_i = (int *)arg, tmp;
  414 
  415         /*
  416          * this is an evil hack to allow broken apps to perform mixer ioctls
  417          * on dsp devices.
  418          */
  419 
  420         d = dsp_get_info(i_dev);
  421         if (IOCGROUP(cmd) == 'M') {
  422                 /*
  423                  * This is at least, a bug to bug compatible with OSS.
  424                  */
  425                 if (d->mixer_dev != NULL)
  426                         return mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
  427                 else
  428                         return EBADF;
  429         }
  430 
  431         getchns(i_dev, &rdch, &wrch, 0);
  432 
  433         kill = 0;
  434         if (wrch && (wrch->flags & CHN_F_DEAD))
  435                 kill |= 1;
  436         if (rdch && (rdch->flags & CHN_F_DEAD))
  437                 kill |= 2;
  438         if (kill == 3) {
  439                 relchns(i_dev, rdch, wrch, 0);
  440                 return EINVAL;
  441         }
  442         if (kill & 1)
  443                 wrch = NULL;
  444         if (kill & 2)
  445                 rdch = NULL;
  446         
  447         switch(cmd) {
  448 #ifdef OLDPCM_IOCTL
  449         /*
  450          * we start with the new ioctl interface.
  451          */
  452         case AIONWRITE: /* how many bytes can write ? */
  453                 if (wrch) {
  454                         CHN_LOCK(wrch);
  455 /*
  456                 if (wrch && wrch->bufhard.dl)
  457                         while (chn_wrfeed(wrch) == 0);
  458 */
  459                         *arg_i = sndbuf_getfree(wrch->bufsoft);
  460                         CHN_UNLOCK(wrch);
  461                 } else {
  462                         *arg_i = 0;
  463                         ret = EINVAL;
  464                 }
  465                 break;
  466 
  467         case AIOSSIZE:     /* set the current blocksize */
  468                 {
  469                         struct snd_size *p = (struct snd_size *)arg;
  470 
  471                         p->play_size = 0;
  472                         p->rec_size = 0;
  473                         if (wrch) {
  474                                 CHN_LOCK(wrch);
  475                                 chn_setblocksize(wrch, 2, p->play_size);
  476                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
  477                                 CHN_UNLOCK(wrch);
  478                         }
  479                         if (rdch) {
  480                                 CHN_LOCK(rdch);
  481                                 chn_setblocksize(rdch, 2, p->rec_size);
  482                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
  483                                 CHN_UNLOCK(rdch);
  484                         }
  485                 }
  486                 break;
  487         case AIOGSIZE:  /* get the current blocksize */
  488                 {
  489                         struct snd_size *p = (struct snd_size *)arg;
  490 
  491                         if (wrch) {
  492                                 CHN_LOCK(wrch);
  493                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
  494                                 CHN_UNLOCK(wrch);
  495                         }
  496                         if (rdch) {
  497                                 CHN_LOCK(rdch);
  498                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
  499                                 CHN_UNLOCK(rdch);
  500                         }
  501                 }
  502                 break;
  503 
  504         case AIOSFMT:
  505         case AIOGFMT:
  506                 {
  507                         snd_chan_param *p = (snd_chan_param *)arg;
  508 
  509                         if (cmd == AIOSFMT &&
  510                             ((p->play_format != 0 && p->play_rate == 0) ||
  511                             (p->rec_format != 0 && p->rec_rate == 0))) {
  512                                 ret = EINVAL;
  513                                 break;
  514                         }
  515                         if (wrch) {
  516                                 CHN_LOCK(wrch);
  517                                 if (cmd == AIOSFMT && p->play_format != 0) {
  518                                         chn_setformat(wrch, p->play_format);
  519                                         chn_setspeed(wrch, p->play_rate);
  520                                 }
  521                                 p->play_rate = wrch->speed;
  522                                 p->play_format = wrch->format;
  523                                 CHN_UNLOCK(wrch);
  524                         } else {
  525                                 p->play_rate = 0;
  526                                 p->play_format = 0;
  527                         }
  528                         if (rdch) {
  529                                 CHN_LOCK(rdch);
  530                                 if (cmd == AIOSFMT && p->rec_format != 0) {
  531                                         chn_setformat(rdch, p->rec_format);
  532                                         chn_setspeed(rdch, p->rec_rate);
  533                                 }
  534                                 p->rec_rate = rdch->speed;
  535                                 p->rec_format = rdch->format;
  536                                 CHN_UNLOCK(rdch);
  537                         } else {
  538                                 p->rec_rate = 0;
  539                                 p->rec_format = 0;
  540                         }
  541                 }
  542                 break;
  543 
  544         case AIOGCAP:     /* get capabilities */
  545                 {
  546                         snd_capabilities *p = (snd_capabilities *)arg;
  547                         struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
  548                         struct cdev *pdev;
  549 
  550                         if (rdch) {
  551                                 CHN_LOCK(rdch);
  552                                 rcaps = chn_getcaps(rdch);
  553                         }
  554                         if (wrch) {
  555                                 CHN_LOCK(wrch);
  556                                 pcaps = chn_getcaps(wrch);
  557                         }
  558                         p->rate_min = max(rcaps? rcaps->minspeed : 0,
  559                                           pcaps? pcaps->minspeed : 0);
  560                         p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
  561                                           pcaps? pcaps->maxspeed : 1000000);
  562                         p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
  563                                          wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
  564                         /* XXX bad on sb16 */
  565                         p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
  566                                      (wrch? chn_getformats(wrch) : 0xffffffff);
  567                         if (rdch && wrch)
  568                                 p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
  569                         pdev = d->mixer_dev;
  570                         p->mixers = 1; /* default: one mixer */
  571                         p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
  572                         p->left = p->right = 100;
  573                         if (rdch)
  574                                 CHN_UNLOCK(rdch);
  575                         if (wrch)
  576                                 CHN_UNLOCK(wrch);
  577                 }
  578                 break;
  579 
  580         case AIOSTOP:
  581                 if (*arg_i == AIOSYNC_PLAY && wrch) {
  582                         CHN_LOCK(wrch);
  583                         *arg_i = chn_abort(wrch);
  584                         CHN_UNLOCK(wrch);
  585                 } else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
  586                         CHN_LOCK(rdch);
  587                         *arg_i = chn_abort(rdch);
  588                         CHN_UNLOCK(rdch);
  589                 } else {
  590                         printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
  591                         *arg_i = 0;
  592                 }
  593                 break;
  594 
  595         case AIOSYNC:
  596                 printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
  597                         ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
  598                 break;
  599 #endif
  600         /*
  601          * here follow the standard ioctls (filio.h etc.)
  602          */
  603         case FIONREAD: /* get # bytes to read */
  604                 if (rdch) {
  605                         CHN_LOCK(rdch);
  606 /*                      if (rdch && rdch->bufhard.dl)
  607                                 while (chn_rdfeed(rdch) == 0);
  608 */
  609                         *arg_i = sndbuf_getready(rdch->bufsoft);
  610                         CHN_UNLOCK(rdch);
  611                 } else {
  612                         *arg_i = 0;
  613                         ret = EINVAL;
  614                 }
  615                 break;
  616 
  617         case FIOASYNC: /*set/clear async i/o */
  618                 DEB( printf("FIOASYNC\n") ; )
  619                 break;
  620 
  621         case SNDCTL_DSP_NONBLOCK:
  622         case FIONBIO: /* set/clear non-blocking i/o */
  623                 if (rdch) {
  624                         CHN_LOCK(rdch);
  625                         if (*arg_i)
  626                                 rdch->flags |= CHN_F_NBIO;
  627                         else
  628                                 rdch->flags &= ~CHN_F_NBIO;
  629                         CHN_UNLOCK(rdch);
  630                 }
  631                 if (wrch) {
  632                         CHN_LOCK(wrch);
  633                         if (*arg_i)
  634                                 wrch->flags |= CHN_F_NBIO;
  635                         else
  636                                 wrch->flags &= ~CHN_F_NBIO;
  637                         CHN_UNLOCK(wrch);
  638                 }
  639                 break;
  640 
  641         /*
  642          * Finally, here is the linux-compatible ioctl interface
  643          */
  644 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
  645         case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
  646         case SNDCTL_DSP_GETBLKSIZE:
  647                 chn = wrch ? wrch : rdch;
  648                 if (chn) {
  649                         CHN_LOCK(chn);
  650                         *arg_i = sndbuf_getblksz(chn->bufsoft);
  651                         CHN_UNLOCK(chn);
  652                 } else {
  653                         *arg_i = 0;
  654                         ret = EINVAL;
  655                 }
  656                 break ;
  657 
  658         case SNDCTL_DSP_SETBLKSIZE:
  659                 RANGE(*arg_i, 16, 65536);
  660                 if (wrch) {
  661                         CHN_LOCK(wrch);
  662                         chn_setblocksize(wrch, 2, *arg_i);
  663                         CHN_UNLOCK(wrch);
  664                 }
  665                 if (rdch) {
  666                         CHN_LOCK(rdch);
  667                         chn_setblocksize(rdch, 2, *arg_i);
  668                         CHN_UNLOCK(rdch);
  669                 }
  670                 break;
  671 
  672         case SNDCTL_DSP_RESET:
  673                 DEB(printf("dsp reset\n"));
  674                 if (wrch) {
  675                         CHN_LOCK(wrch);
  676                         chn_abort(wrch);
  677                         chn_resetbuf(wrch);
  678                         CHN_UNLOCK(wrch);
  679                 }
  680                 if (rdch) {
  681                         CHN_LOCK(rdch);
  682                         chn_abort(rdch);
  683                         chn_resetbuf(rdch);
  684                         CHN_UNLOCK(rdch);
  685                 }
  686                 break;
  687 
  688         case SNDCTL_DSP_SYNC:
  689                 DEB(printf("dsp sync\n"));
  690                 /* chn_sync may sleep */
  691                 if (wrch) {
  692                         CHN_LOCK(wrch);
  693                         chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
  694                         CHN_UNLOCK(wrch);
  695                 }
  696                 break;
  697 
  698         case SNDCTL_DSP_SPEED:
  699                 /* chn_setspeed may sleep */
  700                 tmp = 0;
  701                 if (wrch) {
  702                         CHN_LOCK(wrch);
  703                         ret = chn_setspeed(wrch, *arg_i);
  704                         tmp = wrch->speed;
  705                         CHN_UNLOCK(wrch);
  706                 }
  707                 if (rdch && ret == 0) {
  708                         CHN_LOCK(rdch);
  709                         ret = chn_setspeed(rdch, *arg_i);
  710                         if (tmp == 0)
  711                                 tmp = rdch->speed;
  712                         CHN_UNLOCK(rdch);
  713                 }
  714                 *arg_i = tmp;
  715                 break;
  716 
  717         case SOUND_PCM_READ_RATE:
  718                 chn = wrch ? wrch : rdch;
  719                 if (chn) {
  720                         CHN_LOCK(chn);
  721                         *arg_i = chn->speed;
  722                         CHN_UNLOCK(chn);
  723                 } else {
  724                         *arg_i = 0;
  725                         ret = EINVAL;
  726                 }
  727                 break;
  728 
  729         case SNDCTL_DSP_STEREO:
  730                 tmp = -1;
  731                 *arg_i = (*arg_i)? AFMT_STEREO : 0;
  732                 if (wrch) {
  733                         CHN_LOCK(wrch);
  734                         ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
  735                         tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
  736                         CHN_UNLOCK(wrch);
  737                 }
  738                 if (rdch && ret == 0) {
  739                         CHN_LOCK(rdch);
  740                         ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
  741                         if (tmp == -1)
  742                                 tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
  743                         CHN_UNLOCK(rdch);
  744                 }
  745                 *arg_i = tmp;
  746                 break;
  747 
  748         case SOUND_PCM_WRITE_CHANNELS:
  749 /*      case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
  750                 if (*arg_i != 0) {
  751                         tmp = 0;
  752                         *arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
  753                         if (wrch) {
  754                                 CHN_LOCK(wrch);
  755                                 ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
  756                                 tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
  757                                 CHN_UNLOCK(wrch);
  758                         }
  759                         if (rdch && ret == 0) {
  760                                 CHN_LOCK(rdch);
  761                                 ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
  762                                 if (tmp == 0)
  763                                         tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
  764                                 CHN_UNLOCK(rdch);
  765                         }
  766                         *arg_i = tmp;
  767                 } else {
  768                         chn = wrch ? wrch : rdch;
  769                         CHN_LOCK(chn);
  770                         *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
  771                         CHN_UNLOCK(chn);
  772                 }
  773                 break;
  774 
  775         case SOUND_PCM_READ_CHANNELS:
  776                 chn = wrch ? wrch : rdch;
  777                 if (chn) {
  778                         CHN_LOCK(chn);
  779                         *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
  780                         CHN_UNLOCK(chn);
  781                 } else {
  782                         *arg_i = 0;
  783                         ret = EINVAL;
  784                 }
  785                 break;
  786 
  787         case SNDCTL_DSP_GETFMTS:        /* returns a mask of supported fmts */
  788                 chn = wrch ? wrch : rdch;
  789                 if (chn) {
  790                         CHN_LOCK(chn);
  791                         *arg_i = chn_getformats(chn);
  792                         CHN_UNLOCK(chn);
  793                 } else {
  794                         *arg_i = 0;
  795                         ret = EINVAL;
  796                 }
  797                 break ;
  798 
  799         case SNDCTL_DSP_SETFMT: /* sets _one_ format */
  800                 if ((*arg_i != AFMT_QUERY)) {
  801                         tmp = 0;
  802                         if (wrch) {
  803                                 CHN_LOCK(wrch);
  804                                 ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
  805                                 tmp = wrch->format & ~AFMT_STEREO;
  806                                 CHN_UNLOCK(wrch);
  807                         }
  808                         if (rdch && ret == 0) {
  809                                 CHN_LOCK(rdch);
  810                                 ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
  811                                 if (tmp == 0)
  812                                         tmp = rdch->format & ~AFMT_STEREO;
  813                                 CHN_UNLOCK(rdch);
  814                         }
  815                         *arg_i = tmp;
  816                 } else {
  817                         chn = wrch ? wrch : rdch;
  818                         CHN_LOCK(chn);
  819                         *arg_i = chn->format & ~AFMT_STEREO;
  820                         CHN_UNLOCK(chn);
  821                 }
  822                 break;
  823 
  824         case SNDCTL_DSP_SETFRAGMENT:
  825                 DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
  826                 {
  827                         u_int32_t fragln = (*arg_i) & 0x0000ffff;
  828                         u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
  829                         u_int32_t fragsz;
  830                         u_int32_t r_maxfrags, r_fragsz;
  831 
  832                         RANGE(fragln, 4, 16);
  833                         fragsz = 1 << fragln;
  834 
  835                         if (maxfrags == 0)
  836                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
  837                         if (maxfrags < 2)
  838                                 maxfrags = 2;
  839                         if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
  840                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
  841 
  842                         DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
  843                         if (rdch) {
  844                                 CHN_LOCK(rdch);
  845                                 ret = chn_setblocksize(rdch, maxfrags, fragsz);
  846                                 r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
  847                                 r_fragsz = sndbuf_getblksz(rdch->bufsoft);
  848                                 CHN_UNLOCK(rdch);
  849                         } else {
  850                                 r_maxfrags = maxfrags;
  851                                 r_fragsz = fragsz;
  852                         }
  853                         if (wrch && ret == 0) {
  854                                 CHN_LOCK(wrch);
  855                                 ret = chn_setblocksize(wrch, maxfrags, fragsz);
  856                                 maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
  857                                 fragsz = sndbuf_getblksz(wrch->bufsoft);
  858                                 CHN_UNLOCK(wrch);
  859                         } else { /* use whatever came from the read channel */
  860                                 maxfrags = r_maxfrags;
  861                                 fragsz = r_fragsz;
  862                         }
  863 
  864                         fragln = 0;
  865                         while (fragsz > 1) {
  866                                 fragln++;
  867                                 fragsz >>= 1;
  868                         }
  869                         *arg_i = (maxfrags << 16) | fragln;
  870                 }
  871                 break;
  872 
  873         case SNDCTL_DSP_GETISPACE:
  874                 /* return the size of data available in the input queue */
  875                 {
  876                         audio_buf_info *a = (audio_buf_info *)arg;
  877                         if (rdch) {
  878                                 struct snd_dbuf *bs = rdch->bufsoft;
  879 
  880                                 CHN_LOCK(rdch);
  881                                 a->bytes = sndbuf_getready(bs);
  882                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
  883                                 a->fragstotal = sndbuf_getblkcnt(bs);
  884                                 a->fragsize = sndbuf_getblksz(bs);
  885                                 CHN_UNLOCK(rdch);
  886                         }
  887                 }
  888                 break;
  889 
  890         case SNDCTL_DSP_GETOSPACE:
  891                 /* return space available in the output queue */
  892                 {
  893                         audio_buf_info *a = (audio_buf_info *)arg;
  894                         if (wrch) {
  895                                 struct snd_dbuf *bs = wrch->bufsoft;
  896 
  897                                 CHN_LOCK(wrch);
  898                                 /* XXX abusive DMA update: chn_wrupdate(wrch); */
  899                                 a->bytes = sndbuf_getfree(bs);
  900                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
  901                                 a->fragstotal = sndbuf_getblkcnt(bs);
  902                                 a->fragsize = sndbuf_getblksz(bs);
  903                                 CHN_UNLOCK(wrch);
  904                         }
  905                 }
  906                 break;
  907 
  908         case SNDCTL_DSP_GETIPTR:
  909                 {
  910                         count_info *a = (count_info *)arg;
  911                         if (rdch) {
  912                                 struct snd_dbuf *bs = rdch->bufsoft;
  913 
  914                                 CHN_LOCK(rdch);
  915                                 /* XXX abusive DMA update: chn_rdupdate(rdch); */
  916                                 a->bytes = sndbuf_gettotal(bs);
  917                                 a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
  918                                 a->ptr = sndbuf_getreadyptr(bs);
  919                                 rdch->blocks = sndbuf_getblocks(bs);
  920                                 CHN_UNLOCK(rdch);
  921                         } else
  922                                 ret = EINVAL;
  923                 }
  924                 break;
  925 
  926         case SNDCTL_DSP_GETOPTR:
  927                 {
  928                         count_info *a = (count_info *)arg;
  929                         if (wrch) {
  930                                 struct snd_dbuf *bs = wrch->bufsoft;
  931 
  932                                 CHN_LOCK(wrch);
  933                                 /* XXX abusive DMA update: chn_wrupdate(wrch); */
  934                                 a->bytes = sndbuf_gettotal(bs);
  935                                 a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
  936                                 a->ptr = sndbuf_getreadyptr(bs);
  937                                 wrch->blocks = sndbuf_getblocks(bs);
  938                                 CHN_UNLOCK(wrch);
  939                         } else
  940                                 ret = EINVAL;
  941                 }
  942                 break;
  943 
  944         case SNDCTL_DSP_GETCAPS:
  945                 *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
  946                 if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
  947                         *arg_i |= DSP_CAP_DUPLEX;
  948                 break;
  949 
  950         case SOUND_PCM_READ_BITS:
  951                 chn = wrch ? wrch : rdch;
  952                 if (chn) {
  953                         CHN_LOCK(chn);
  954                         if (chn->format & AFMT_8BIT)
  955                                 *arg_i = 8;
  956                         else if (chn->format & AFMT_16BIT)
  957                                 *arg_i = 16;
  958                         else if (chn->format & AFMT_24BIT)
  959                                 *arg_i = 24;
  960                         else if (chn->format & AFMT_32BIT)
  961                                 *arg_i = 32;
  962                         else
  963                                 ret = EINVAL;
  964                         CHN_UNLOCK(chn);
  965                 } else {
  966                         *arg_i = 0;
  967                         ret = EINVAL;
  968                 }
  969                 break;
  970 
  971         case SNDCTL_DSP_SETTRIGGER:
  972                 if (rdch) {
  973                         CHN_LOCK(rdch);
  974                         rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
  975                         if (*arg_i & PCM_ENABLE_INPUT)
  976                                 chn_start(rdch, 1);
  977                         else
  978                                 rdch->flags |= CHN_F_NOTRIGGER;
  979                         CHN_UNLOCK(rdch);
  980                 }
  981                 if (wrch) {
  982                         CHN_LOCK(wrch);
  983                         wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
  984                         if (*arg_i & PCM_ENABLE_OUTPUT)
  985                                 chn_start(wrch, 1);
  986                         else
  987                                 wrch->flags |= CHN_F_NOTRIGGER;
  988                         CHN_UNLOCK(wrch);
  989                 }
  990                 break;
  991 
  992         case SNDCTL_DSP_GETTRIGGER:
  993                 *arg_i = 0;
  994                 if (wrch) {
  995                         CHN_LOCK(wrch);
  996                         if (wrch->flags & CHN_F_TRIGGERED)
  997                                 *arg_i |= PCM_ENABLE_OUTPUT;
  998                         CHN_UNLOCK(wrch);
  999                 }
 1000                 if (rdch) {
 1001                         CHN_LOCK(rdch);
 1002                         if (rdch->flags & CHN_F_TRIGGERED)
 1003                                 *arg_i |= PCM_ENABLE_INPUT;
 1004                         CHN_UNLOCK(rdch);
 1005                 }
 1006                 break;
 1007 
 1008         case SNDCTL_DSP_GETODELAY:
 1009                 if (wrch) {
 1010                         struct snd_dbuf *b = wrch->bufhard;
 1011                         struct snd_dbuf *bs = wrch->bufsoft;
 1012 
 1013                         CHN_LOCK(wrch);
 1014                         /* XXX abusive DMA update: chn_wrupdate(wrch); */
 1015                         *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
 1016                         CHN_UNLOCK(wrch);
 1017                 } else
 1018                         ret = EINVAL;
 1019                 break;
 1020 
 1021         case SNDCTL_DSP_POST:
 1022                 if (wrch) {
 1023                         CHN_LOCK(wrch);
 1024                         wrch->flags &= ~CHN_F_NOTRIGGER;
 1025                         chn_start(wrch, 1);
 1026                         CHN_UNLOCK(wrch);
 1027                 }
 1028                 break;
 1029 
 1030         case SNDCTL_DSP_SETDUPLEX:
 1031                 /*
 1032                  * switch to full-duplex mode if card is in half-duplex
 1033                  * mode and is able to work in full-duplex mode
 1034                  */
 1035                 if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
 1036                         dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
 1037                 break;
 1038 
 1039         case SNDCTL_DSP_MAPINBUF:
 1040         case SNDCTL_DSP_MAPOUTBUF:
 1041         case SNDCTL_DSP_SETSYNCRO:
 1042                 /* undocumented */
 1043 
 1044         case SNDCTL_DSP_SUBDIVIDE:
 1045         case SOUND_PCM_WRITE_FILTER:
 1046         case SOUND_PCM_READ_FILTER:
 1047                 /* dunno what these do, don't sound important */
 1048 
 1049         default:
 1050                 DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
 1051                 ret = EINVAL;
 1052                 break;
 1053         }
 1054         relchns(i_dev, rdch, wrch, 0);
 1055         return ret;
 1056 }
 1057 
 1058 static int
 1059 dsp_poll(struct cdev *i_dev, int events, struct thread *td)
 1060 {
 1061         struct pcm_channel *wrch = NULL, *rdch = NULL;
 1062         int ret, e;
 1063 
 1064         ret = 0;
 1065         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1066 
 1067         if (wrch) {
 1068                 e = (events & (POLLOUT | POLLWRNORM));
 1069                 if (e)
 1070                         ret |= chn_poll(wrch, e, td);
 1071         }
 1072         if (rdch) {
 1073                 e = (events & (POLLIN | POLLRDNORM));
 1074                 if (e)
 1075                         ret |= chn_poll(rdch, e, td);
 1076         }
 1077         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1078 
 1079         return ret;
 1080 }
 1081 
 1082 static int
 1083 dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
 1084 {
 1085         struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
 1086 
 1087         /*
 1088          * Reject PROT_EXEC by default. It just doesn't makes sense.
 1089          * Unfortunately, we have to give up this one due to linux_mmap
 1090          * changes.
 1091          *
 1092          * http://lists.freebsd.org/pipermail/freebsd-emulation/2007-June/003698.html
 1093          *
 1094          */
 1095         if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec == 0)
 1096                 return -1;
 1097 
 1098         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1099 #if 0
 1100         /*
 1101          * XXX the linux api uses the nprot to select read/write buffer
 1102          * our vm system doesn't allow this, so force write buffer
 1103          */
 1104 
 1105         if (wrch && (nprot & PROT_WRITE)) {
 1106                 c = wrch;
 1107         } else if (rdch && (nprot & PROT_READ)) {
 1108                 c = rdch;
 1109         } else {
 1110                 return -1;
 1111         }
 1112 #else
 1113         c = wrch;
 1114 #endif
 1115 
 1116         if (c == NULL) {
 1117                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1118                 return -1;
 1119         }
 1120 
 1121         if (offset >= sndbuf_getsize(c->bufsoft)) {
 1122                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1123                 return -1;
 1124         }
 1125 
 1126         if (!(c->flags & CHN_F_MAPPED))
 1127                 c->flags |= CHN_F_MAPPED;
 1128 
 1129         *paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset));
 1130         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1131 
 1132         return 0;
 1133 }
 1134 
 1135 #ifdef USING_DEVFS
 1136 
 1137 /*
 1138  * Clone logic is this:
 1139  * x E X = {dsp, dspW, audio}
 1140  * x -> x${sysctl("hw.snd.unit")}
 1141  * xN->
 1142  *    for i N = 1 to channels of device N
 1143  *      if xN.i isn't busy, return its dev_t
 1144  */
 1145 static void
 1146 dsp_clone(void *arg, struct ucred *cred, char *name, int namelen,
 1147     struct cdev **dev)
 1148 {
 1149         struct cdev *pdev;
 1150         struct snddev_info *pcm_dev;
 1151         struct snddev_channel *pcm_chan;
 1152         int i, unit, devtype;
 1153         static int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
 1154         static char *devnames[3] = {"dsp", "dspW", "audio"};
 1155 
 1156         if (*dev != NULL)
 1157                 return;
 1158         if (pcm_devclass == NULL)
 1159                 return;
 1160 
 1161         devtype = 0;
 1162         unit = -1;
 1163         for (i = 0; (i < 3) && (unit == -1); i++) {
 1164                 devtype = devtypes[i];
 1165                 if (strcmp(name, devnames[i]) == 0) {
 1166                         unit = snd_unit;
 1167                 } else {
 1168                         if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
 1169                                 unit = -1;
 1170                 }
 1171         }
 1172         if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
 1173                 return;
 1174 
 1175         pcm_dev = devclass_get_softc(pcm_devclass, unit);
 1176 
 1177         if (pcm_dev == NULL)
 1178                 return;
 1179 
 1180         SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) {
 1181 
 1182                 switch(devtype) {
 1183                         case SND_DEV_DSP:
 1184                                 pdev = pcm_chan->dsp_devt;
 1185                                 break;
 1186                         case SND_DEV_DSP16:
 1187                                 pdev = pcm_chan->dspW_devt;
 1188                                 break;
 1189                         case SND_DEV_AUDIO:
 1190                                 pdev = pcm_chan->audio_devt;
 1191                                 break;
 1192                         default:
 1193                                 panic("Unknown devtype %d", devtype);
 1194                 }
 1195 
 1196                 if ((pdev != NULL) && (pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
 1197                         *dev = pdev;
 1198                         dev_ref(*dev);
 1199                         return;
 1200                 }
 1201         }
 1202 }
 1203 
 1204 static void
 1205 dsp_sysinit(void *p)
 1206 {
 1207         dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
 1208 }
 1209 
 1210 static void
 1211 dsp_sysuninit(void *p)
 1212 {
 1213         if (dsp_ehtag != NULL)
 1214                 EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
 1215 }
 1216 
 1217 SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
 1218 SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
 1219 #endif
 1220 
 1221 

Cache object: 21726c38e50076e79b7c6a4794e3f8e0


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