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

Cache object: a9bdf9d785eb5e4fa8db46ecf3e4141d


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