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

Cache object: e93f4d8961090a8829b78a5c0b66957f


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