The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/sound/pcm/dsp.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/param.h>
   28 #include <sys/queue.h>
   29 
   30 #include <dev/sound/pcm/sound.h>
   31 
   32 SND_DECLARE_FILE("$FreeBSD$");
   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         .d_maj =        SND_CDEV_MAJOR,
   56 };
   57 
   58 #ifdef USING_DEVFS
   59 static eventhandler_tag dsp_ehtag;
   60 #endif
   61 
   62 static struct snddev_info *
   63 dsp_get_info(struct cdev *dev)
   64 {
   65         struct snddev_info *d;
   66         int unit;
   67 
   68         unit = PCMUNIT(dev);
   69         if (unit >= devclass_get_maxunit(pcm_devclass))
   70                 return NULL;
   71         d = devclass_get_softc(pcm_devclass, unit);
   72 
   73         return d;
   74 }
   75 
   76 static u_int32_t
   77 dsp_get_flags(struct cdev *dev)
   78 {
   79         device_t bdev;
   80         int unit;
   81 
   82         unit = PCMUNIT(dev);
   83         if (unit >= devclass_get_maxunit(pcm_devclass))
   84                 return 0xffffffff;
   85         bdev = devclass_get_device(pcm_devclass, unit);
   86 
   87         return pcm_getflags(bdev);
   88 }
   89 
   90 static void
   91 dsp_set_flags(struct cdev *dev, u_int32_t flags)
   92 {
   93         device_t bdev;
   94         int unit;
   95 
   96         unit = PCMUNIT(dev);
   97         if (unit >= devclass_get_maxunit(pcm_devclass))
   98                 return;
   99         bdev = devclass_get_device(pcm_devclass, unit);
  100 
  101         pcm_setflags(bdev, flags);
  102 }
  103 
  104 /*
  105  * return the channels channels associated with an open device instance.
  106  * set the priority if the device is simplex and one direction (only) is
  107  * specified.
  108  * lock channels specified.
  109  */
  110 static int
  111 getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
  112 {
  113         struct snddev_info *d;
  114         u_int32_t flags;
  115 
  116         flags = dsp_get_flags(dev);
  117         d = dsp_get_info(dev);
  118         pcm_inprog(d, 1);
  119         pcm_lock(d);
  120         KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
  121                 ("getchns: read and write both prioritised"));
  122 
  123         if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
  124                 flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
  125                 dsp_set_flags(dev, flags);
  126         }
  127 
  128         *rdch = dev->si_drv1;
  129         *wrch = dev->si_drv2;
  130         if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
  131                 if (prio) {
  132                         if (*rdch && flags & SD_F_PRIO_WR) {
  133                                 dev->si_drv1 = NULL;
  134                                 *rdch = pcm_getfakechan(d);
  135                         } else if (*wrch && flags & SD_F_PRIO_RD) {
  136                                 dev->si_drv2 = NULL;
  137                                 *wrch = pcm_getfakechan(d);
  138                         }
  139                 }
  140 
  141                 pcm_getfakechan(d)->flags |= CHN_F_BUSY;
  142         }
  143         pcm_unlock(d);
  144 
  145         if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
  146                 CHN_LOCK(*rdch);
  147         if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
  148                 CHN_LOCK(*wrch);
  149 
  150         return 0;
  151 }
  152 
  153 /* unlock specified channels */
  154 static void
  155 relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
  156 {
  157         struct snddev_info *d;
  158 
  159         d = dsp_get_info(dev);
  160         if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
  161                 CHN_UNLOCK(wrch);
  162         if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
  163                 CHN_UNLOCK(rdch);
  164         pcm_inprog(d, -1);
  165 }
  166 
  167 static int
  168 dsp_open(struct cdev *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(struct cdev *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(struct cdev *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(struct cdev *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(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
  446 {
  447         struct pcm_channel *chn, *rdch, *wrch;
  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         d = dsp_get_info(i_dev);
  459         if (IOCGROUP(cmd) == 'M')
  460                 return mixer_ioctl(d->mixer_dev, cmd, arg, mode, td);
  461 
  462         s = spltty();
  463         getchns(i_dev, &rdch, &wrch, 0);
  464 
  465         kill = 0;
  466         if (wrch && (wrch->flags & CHN_F_DEAD))
  467                 kill |= 1;
  468         if (rdch && (rdch->flags & CHN_F_DEAD))
  469                 kill |= 2;
  470         if (kill == 3) {
  471                 relchns(i_dev, rdch, wrch, 0);
  472                 splx(s);
  473                 return EINVAL;
  474         }
  475         if (kill & 1)
  476                 wrch = NULL;
  477         if (kill & 2)
  478                 rdch = NULL;
  479         
  480         switch(cmd) {
  481 #ifdef OLDPCM_IOCTL
  482         /*
  483          * we start with the new ioctl interface.
  484          */
  485         case AIONWRITE: /* how many bytes can write ? */
  486                 CHN_LOCK(wrch);
  487 /*
  488                 if (wrch && wrch->bufhard.dl)
  489                         while (chn_wrfeed(wrch) == 0);
  490 */
  491                 *arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0;
  492                 CHN_UNLOCK(wrch);
  493                 break;
  494 
  495         case AIOSSIZE:     /* set the current blocksize */
  496                 {
  497                         struct snd_size *p = (struct snd_size *)arg;
  498 
  499                         p->play_size = 0;
  500                         p->rec_size = 0;
  501                         if (wrch) {
  502                                 CHN_LOCK(wrch);
  503                                 chn_setblocksize(wrch, 2, p->play_size);
  504                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
  505                                 CHN_UNLOCK(wrch);
  506                         }
  507                         if (rdch) {
  508                                 CHN_LOCK(rdch);
  509                                 chn_setblocksize(rdch, 2, p->rec_size);
  510                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
  511                                 CHN_UNLOCK(rdch);
  512                         }
  513                 }
  514                 break;
  515         case AIOGSIZE:  /* get the current blocksize */
  516                 {
  517                         struct snd_size *p = (struct snd_size *)arg;
  518 
  519                         if (wrch) {
  520                                 CHN_LOCK(wrch);
  521                                 p->play_size = sndbuf_getblksz(wrch->bufsoft);
  522                                 CHN_UNLOCK(wrch);
  523                         }
  524                         if (rdch) {
  525                                 CHN_LOCK(rdch);
  526                                 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
  527                                 CHN_UNLOCK(rdch);
  528                         }
  529                 }
  530                 break;
  531 
  532         case AIOSFMT:
  533         case AIOGFMT:
  534                 {
  535                         snd_chan_param *p = (snd_chan_param *)arg;
  536 
  537                         if (wrch) {
  538                                 CHN_LOCK(wrch);
  539                                 if (cmd == AIOSFMT) {
  540                                         chn_setformat(wrch, p->play_format);
  541                                         chn_setspeed(wrch, p->play_rate);
  542                                 }
  543                                 p->play_rate = wrch->speed;
  544                                 p->play_format = wrch->format;
  545                                 CHN_UNLOCK(wrch);
  546                         } else {
  547                                 p->play_rate = 0;
  548                                 p->play_format = 0;
  549                         }
  550                         if (rdch) {
  551                                 CHN_LOCK(rdch);
  552                                 if (cmd == AIOSFMT) {
  553                                         chn_setformat(rdch, p->rec_format);
  554                                         chn_setspeed(rdch, p->rec_rate);
  555                                 }
  556                                 p->rec_rate = rdch->speed;
  557                                 p->rec_format = rdch->format;
  558                                 CHN_UNLOCK(rdch);
  559                         } else {
  560                                 p->rec_rate = 0;
  561                                 p->rec_format = 0;
  562                         }
  563                 }
  564                 break;
  565 
  566         case AIOGCAP:     /* get capabilities */
  567                 {
  568                         snd_capabilities *p = (snd_capabilities *)arg;
  569                         struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
  570                         struct cdev *pdev;
  571 
  572                         if (rdch) {
  573                                 CHN_LOCK(rdch);
  574                                 rcaps = chn_getcaps(rdch);
  575                         }
  576                         if (wrch) {
  577                                 CHN_LOCK(wrch);
  578                                 pcaps = chn_getcaps(wrch);
  579                         }
  580                         p->rate_min = max(rcaps? rcaps->minspeed : 0,
  581                                           pcaps? pcaps->minspeed : 0);
  582                         p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
  583                                           pcaps? pcaps->maxspeed : 1000000);
  584                         p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
  585                                          wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
  586                         /* XXX bad on sb16 */
  587                         p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
  588                                      (wrch? chn_getformats(wrch) : 0xffffffff);
  589                         if (rdch && wrch)
  590                                 p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
  591                         pdev = d->mixer_dev;
  592                         p->mixers = 1; /* default: one mixer */
  593                         p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
  594                         p->left = p->right = 100;
  595                         if (rdch)
  596                                 CHN_UNLOCK(rdch);
  597                         if (wrch)
  598                                 CHN_UNLOCK(wrch);
  599                 }
  600                 break;
  601 
  602         case AIOSTOP:
  603                 if (*arg_i == AIOSYNC_PLAY && wrch) {
  604                         CHN_LOCK(wrch);
  605                         *arg_i = chn_abort(wrch);
  606                         CHN_UNLOCK(wrch);
  607                 } else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
  608                         CHN_LOCK(rdch);
  609                         *arg_i = chn_abort(rdch);
  610                         CHN_UNLOCK(rdch);
  611                 } else {
  612                         printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
  613                         *arg_i = 0;
  614                 }
  615                 break;
  616 
  617         case AIOSYNC:
  618                 printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
  619                         ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
  620                 break;
  621 #endif
  622         /*
  623          * here follow the standard ioctls (filio.h etc.)
  624          */
  625         case FIONREAD: /* get # bytes to read */
  626                 if (rdch) {
  627                         CHN_LOCK(rdch);
  628 /*                      if (rdch && rdch->bufhard.dl)
  629                                 while (chn_rdfeed(rdch) == 0);
  630 */
  631                         *arg_i = sndbuf_getready(rdch->bufsoft);
  632                         CHN_UNLOCK(rdch);
  633                 } else
  634                         *arg_i = 0;
  635                 break;
  636 
  637         case FIOASYNC: /*set/clear async i/o */
  638                 DEB( printf("FIOASYNC\n") ; )
  639                 break;
  640 
  641         case SNDCTL_DSP_NONBLOCK:
  642         case FIONBIO: /* set/clear non-blocking i/o */
  643                 if (rdch) {
  644                         CHN_LOCK(rdch);
  645                         if (*arg_i)
  646                                 rdch->flags |= CHN_F_NBIO;
  647                         else
  648                                 rdch->flags &= ~CHN_F_NBIO;
  649                         CHN_UNLOCK(rdch);
  650                 }
  651                 if (wrch) {
  652                         CHN_LOCK(wrch);
  653                         if (*arg_i)
  654                                 wrch->flags |= CHN_F_NBIO;
  655                         else
  656                                 wrch->flags &= ~CHN_F_NBIO;
  657                         CHN_UNLOCK(wrch);
  658                 }
  659                 break;
  660 
  661         /*
  662          * Finally, here is the linux-compatible ioctl interface
  663          */
  664 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
  665         case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
  666         case SNDCTL_DSP_GETBLKSIZE:
  667                 chn = wrch ? wrch : rdch;
  668                 CHN_LOCK(chn);
  669                 *arg_i = sndbuf_getblksz(chn->bufsoft);
  670                 CHN_UNLOCK(chn);
  671                 break ;
  672 
  673         case SNDCTL_DSP_SETBLKSIZE:
  674                 RANGE(*arg_i, 16, 65536);
  675                 if (wrch) {
  676                         CHN_LOCK(wrch);
  677                         chn_setblocksize(wrch, 2, *arg_i);
  678                         CHN_UNLOCK(wrch);
  679                 }
  680                 if (rdch) {
  681                         CHN_LOCK(rdch);
  682                         chn_setblocksize(rdch, 2, *arg_i);
  683                         CHN_UNLOCK(rdch);
  684                 }
  685                 break;
  686 
  687         case SNDCTL_DSP_RESET:
  688                 DEB(printf("dsp reset\n"));
  689                 if (wrch) {
  690                         CHN_LOCK(wrch);
  691                         chn_abort(wrch);
  692                         chn_resetbuf(wrch);
  693                         CHN_UNLOCK(wrch);
  694                 }
  695                 if (rdch) {
  696                         CHN_LOCK(rdch);
  697                         chn_abort(rdch);
  698                         chn_resetbuf(rdch);
  699                         CHN_UNLOCK(rdch);
  700                 }
  701                 break;
  702 
  703         case SNDCTL_DSP_SYNC:
  704                 DEB(printf("dsp sync\n"));
  705                 /* chn_sync may sleep */
  706                 if (wrch) {
  707                         CHN_LOCK(wrch);
  708                         chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
  709                         CHN_UNLOCK(wrch);
  710                 }
  711                 break;
  712 
  713         case SNDCTL_DSP_SPEED:
  714                 /* chn_setspeed may sleep */
  715                 tmp = 0;
  716                 if (wrch) {
  717                         CHN_LOCK(wrch);
  718                         ret = chn_setspeed(wrch, *arg_i);
  719                         tmp = wrch->speed;
  720                         CHN_UNLOCK(wrch);
  721                 }
  722                 if (rdch && ret == 0) {
  723                         CHN_LOCK(rdch);
  724                         ret = chn_setspeed(rdch, *arg_i);
  725                         if (tmp == 0)
  726                                 tmp = rdch->speed;
  727                         CHN_UNLOCK(rdch);
  728                 }
  729                 *arg_i = tmp;
  730                 break;
  731 
  732         case SOUND_PCM_READ_RATE:
  733                 chn = wrch ? wrch : rdch;
  734                 CHN_LOCK(chn);
  735                 *arg_i = chn->speed;
  736                 CHN_UNLOCK(chn);
  737                 break;
  738 
  739         case SNDCTL_DSP_STEREO:
  740                 tmp = -1;
  741                 *arg_i = (*arg_i)? AFMT_STEREO : 0;
  742                 if (wrch) {
  743                         CHN_LOCK(wrch);
  744                         ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
  745                         tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
  746                         CHN_UNLOCK(wrch);
  747                 }
  748                 if (rdch && ret == 0) {
  749                         CHN_LOCK(rdch);
  750                         ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
  751                         if (tmp == -1)
  752                                 tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
  753                         CHN_UNLOCK(rdch);
  754                 }
  755                 *arg_i = tmp;
  756                 break;
  757 
  758         case SOUND_PCM_WRITE_CHANNELS:
  759 /*      case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
  760                 if (*arg_i != 0) {
  761                         tmp = 0;
  762                         *arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
  763                         if (wrch) {
  764                                 CHN_LOCK(wrch);
  765                                 ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
  766                                 tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
  767                                 CHN_UNLOCK(wrch);
  768                         }
  769                         if (rdch && ret == 0) {
  770                                 CHN_LOCK(rdch);
  771                                 ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
  772                                 if (tmp == 0)
  773                                         tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
  774                                 CHN_UNLOCK(rdch);
  775                         }
  776                         *arg_i = tmp;
  777                 } else {
  778                         chn = wrch ? wrch : rdch;
  779                         CHN_LOCK(chn);
  780                         *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
  781                         CHN_UNLOCK(chn);
  782                 }
  783                 break;
  784 
  785         case SOUND_PCM_READ_CHANNELS:
  786                 chn = wrch ? wrch : rdch;
  787                 CHN_LOCK(chn);
  788                 *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
  789                 CHN_UNLOCK(chn);
  790                 break;
  791 
  792         case SNDCTL_DSP_GETFMTS:        /* returns a mask of supported fmts */
  793                 chn = wrch ? wrch : rdch;
  794                 CHN_LOCK(chn);
  795                 *arg_i = chn_getformats(chn);
  796                 CHN_UNLOCK(chn);
  797                 break ;
  798 
  799         case SNDCTL_DSP_SETFMT: /* sets _one_ format */
  800                 if ((*arg_i != AFMT_QUERY)) {
  801                         tmp = 0;
  802                         if (wrch) {
  803                                 CHN_LOCK(wrch);
  804                                 ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
  805                                 tmp = wrch->format & ~AFMT_STEREO;
  806                                 CHN_UNLOCK(wrch);
  807                         }
  808                         if (rdch && ret == 0) {
  809                                 CHN_LOCK(rdch);
  810                                 ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
  811                                 if (tmp == 0)
  812                                         tmp = rdch->format & ~AFMT_STEREO;
  813                                 CHN_UNLOCK(rdch);
  814                         }
  815                         *arg_i = tmp;
  816                 } else {
  817                         chn = wrch ? wrch : rdch;
  818                         CHN_LOCK(chn);
  819                         *arg_i = chn->format & ~AFMT_STEREO;
  820                         CHN_UNLOCK(chn);
  821                 }
  822                 break;
  823 
  824         case SNDCTL_DSP_SETFRAGMENT:
  825                 DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
  826                 {
  827                         u_int32_t fragln = (*arg_i) & 0x0000ffff;
  828                         u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
  829                         u_int32_t fragsz;
  830 
  831                         RANGE(fragln, 4, 16);
  832                         fragsz = 1 << fragln;
  833 
  834                         if (maxfrags == 0)
  835                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
  836                         if (maxfrags < 2)
  837                                 maxfrags = 2;
  838                         if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
  839                                 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
  840 
  841                         DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
  842                         if (rdch) {
  843                                 CHN_LOCK(rdch);
  844                                 ret = chn_setblocksize(rdch, maxfrags, fragsz);
  845                                 maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
  846                                 fragsz = sndbuf_getblksz(rdch->bufsoft);
  847                                 CHN_UNLOCK(rdch);
  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                         }
  856 
  857                         fragln = 0;
  858                         while (fragsz > 1) {
  859                                 fragln++;
  860                                 fragsz >>= 1;
  861                         }
  862                         *arg_i = (maxfrags << 16) | fragln;
  863                 }
  864                 break;
  865 
  866         case SNDCTL_DSP_GETISPACE:
  867                 /* return the size of data available in the input queue */
  868                 {
  869                         audio_buf_info *a = (audio_buf_info *)arg;
  870                         if (rdch) {
  871                                 struct snd_dbuf *bs = rdch->bufsoft;
  872 
  873                                 CHN_LOCK(rdch);
  874                                 a->bytes = sndbuf_getready(bs);
  875                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
  876                                 a->fragstotal = sndbuf_getblkcnt(bs);
  877                                 a->fragsize = sndbuf_getblksz(bs);
  878                                 CHN_UNLOCK(rdch);
  879                         }
  880                 }
  881                 break;
  882 
  883         case SNDCTL_DSP_GETOSPACE:
  884                 /* return space available in the output queue */
  885                 {
  886                         audio_buf_info *a = (audio_buf_info *)arg;
  887                         if (wrch) {
  888                                 struct snd_dbuf *bs = wrch->bufsoft;
  889 
  890                                 CHN_LOCK(wrch);
  891                                 chn_wrupdate(wrch);
  892                                 a->bytes = sndbuf_getfree(bs);
  893                                 a->fragments = a->bytes / sndbuf_getblksz(bs);
  894                                 a->fragstotal = sndbuf_getblkcnt(bs);
  895                                 a->fragsize = sndbuf_getblksz(bs);
  896                                 CHN_UNLOCK(wrch);
  897                         }
  898                 }
  899                 break;
  900 
  901         case SNDCTL_DSP_GETIPTR:
  902                 {
  903                         count_info *a = (count_info *)arg;
  904                         if (rdch) {
  905                                 struct snd_dbuf *bs = rdch->bufsoft;
  906 
  907                                 CHN_LOCK(rdch);
  908                                 chn_rdupdate(rdch);
  909                                 a->bytes = sndbuf_gettotal(bs);
  910                                 a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
  911                                 a->ptr = sndbuf_getreadyptr(bs);
  912                                 rdch->blocks = sndbuf_getblocks(bs);
  913                                 CHN_UNLOCK(rdch);
  914                         } else
  915                                 ret = EINVAL;
  916                 }
  917                 break;
  918 
  919         case SNDCTL_DSP_GETOPTR:
  920                 {
  921                         count_info *a = (count_info *)arg;
  922                         if (wrch) {
  923                                 struct snd_dbuf *bs = wrch->bufsoft;
  924 
  925                                 CHN_LOCK(wrch);
  926                                 chn_wrupdate(wrch);
  927                                 a->bytes = sndbuf_gettotal(bs);
  928                                 a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
  929                                 a->ptr = sndbuf_getreadyptr(bs);
  930                                 wrch->blocks = sndbuf_getblocks(bs);
  931                                 CHN_UNLOCK(wrch);
  932                         } else
  933                                 ret = EINVAL;
  934                 }
  935                 break;
  936 
  937         case SNDCTL_DSP_GETCAPS:
  938                 *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
  939                 if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
  940                         *arg_i |= DSP_CAP_DUPLEX;
  941                 break;
  942 
  943         case SOUND_PCM_READ_BITS:
  944                 chn = wrch ? wrch : rdch;
  945                 CHN_LOCK(chn);
  946                 *arg_i = (chn->format & AFMT_16BIT) ? 16 : 8;
  947                 CHN_UNLOCK(chn);
  948                 break;
  949 
  950         case SNDCTL_DSP_SETTRIGGER:
  951                 if (rdch) {
  952                         CHN_LOCK(rdch);
  953                         rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
  954                         if (*arg_i & PCM_ENABLE_INPUT)
  955                                 chn_start(rdch, 1);
  956                         else
  957                                 rdch->flags |= CHN_F_NOTRIGGER;
  958                         CHN_UNLOCK(rdch);
  959                 }
  960                 if (wrch) {
  961                         CHN_LOCK(wrch);
  962                         wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
  963                         if (*arg_i & PCM_ENABLE_OUTPUT)
  964                                 chn_start(wrch, 1);
  965                         else
  966                                 wrch->flags |= CHN_F_NOTRIGGER;
  967                         CHN_UNLOCK(wrch);
  968                 }
  969                 break;
  970 
  971         case SNDCTL_DSP_GETTRIGGER:
  972                 *arg_i = 0;
  973                 if (wrch) {
  974                         CHN_LOCK(wrch);
  975                         if (wrch->flags & CHN_F_TRIGGERED)
  976                                 *arg_i |= PCM_ENABLE_OUTPUT;
  977                         CHN_UNLOCK(wrch);
  978                 }
  979                 if (rdch) {
  980                         CHN_LOCK(rdch);
  981                         if (rdch->flags & CHN_F_TRIGGERED)
  982                                 *arg_i |= PCM_ENABLE_INPUT;
  983                         CHN_UNLOCK(rdch);
  984                 }
  985                 break;
  986 
  987         case SNDCTL_DSP_GETODELAY:
  988                 if (wrch) {
  989                         struct snd_dbuf *b = wrch->bufhard;
  990                         struct snd_dbuf *bs = wrch->bufsoft;
  991 
  992                         CHN_LOCK(wrch);
  993                         chn_wrupdate(wrch);
  994                         *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
  995                         CHN_UNLOCK(wrch);
  996                 } else
  997                         ret = EINVAL;
  998                 break;
  999 
 1000         case SNDCTL_DSP_POST:
 1001                 if (wrch) {
 1002                         CHN_LOCK(wrch);
 1003                         wrch->flags &= ~CHN_F_NOTRIGGER;
 1004                         chn_start(wrch, 1);
 1005                         CHN_UNLOCK(wrch);
 1006                 }
 1007                 break;
 1008 
 1009         case SNDCTL_DSP_SETDUPLEX:
 1010                 /*
 1011                  * switch to full-duplex mode if card is in half-duplex
 1012                  * mode and is able to work in full-duplex mode
 1013                  */
 1014                 if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
 1015                         dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
 1016                 break;
 1017 
 1018         case SNDCTL_DSP_MAPINBUF:
 1019         case SNDCTL_DSP_MAPOUTBUF:
 1020         case SNDCTL_DSP_SETSYNCRO:
 1021                 /* undocumented */
 1022 
 1023         case SNDCTL_DSP_SUBDIVIDE:
 1024         case SOUND_PCM_WRITE_FILTER:
 1025         case SOUND_PCM_READ_FILTER:
 1026                 /* dunno what these do, don't sound important */
 1027 
 1028         default:
 1029                 DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
 1030                 ret = EINVAL;
 1031                 break;
 1032         }
 1033         relchns(i_dev, rdch, wrch, 0);
 1034         splx(s);
 1035         return ret;
 1036 }
 1037 
 1038 static int
 1039 dsp_poll(struct cdev *i_dev, int events, struct thread *td)
 1040 {
 1041         struct pcm_channel *wrch = NULL, *rdch = NULL;
 1042         intrmask_t s;
 1043         int ret, e;
 1044 
 1045         s = spltty();
 1046         ret = 0;
 1047         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1048 
 1049         if (wrch) {
 1050                 e = (events & (POLLOUT | POLLWRNORM));
 1051                 if (e)
 1052                         ret |= chn_poll(wrch, e, td);
 1053         }
 1054         if (rdch) {
 1055                 e = (events & (POLLIN | POLLRDNORM));
 1056                 if (e)
 1057                         ret |= chn_poll(rdch, e, td);
 1058         }
 1059         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1060 
 1061         splx(s);
 1062         return ret;
 1063 }
 1064 
 1065 static int
 1066 dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
 1067 {
 1068         struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
 1069         intrmask_t s;
 1070 
 1071         if (nprot & PROT_EXEC)
 1072                 return -1;
 1073 
 1074         s = spltty();
 1075         getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1076 #if 0
 1077         /*
 1078          * XXX the linux api uses the nprot to select read/write buffer
 1079          * our vm system doesn't allow this, so force write buffer
 1080          */
 1081 
 1082         if (wrch && (nprot & PROT_WRITE)) {
 1083                 c = wrch;
 1084         } else if (rdch && (nprot & PROT_READ)) {
 1085                 c = rdch;
 1086         } else {
 1087                 splx(s);
 1088                 return -1;
 1089         }
 1090 #else
 1091         c = wrch;
 1092 #endif
 1093 
 1094         if (c == NULL) {
 1095                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1096                 splx(s);
 1097                 return -1;
 1098         }
 1099 
 1100         if (offset >= sndbuf_getsize(c->bufsoft)) {
 1101                 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1102                 splx(s);
 1103                 return -1;
 1104         }
 1105 
 1106         if (!(c->flags & CHN_F_MAPPED))
 1107                 c->flags |= CHN_F_MAPPED;
 1108 
 1109         *paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset));
 1110         relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
 1111 
 1112         splx(s);
 1113         return 0;
 1114 }
 1115 
 1116 #ifdef USING_DEVFS
 1117 
 1118 /*
 1119  * Clone logic is this:
 1120  * x E X = {dsp, dspW, audio}
 1121  * x -> x${sysctl("hw.snd.unit")}
 1122  * xN->
 1123  *    for i N = 1 to channels of device N
 1124  *      if xN.i isn't busy, return its dev_t
 1125  */
 1126 static void
 1127 dsp_clone(void *arg, char *name, int namelen, struct cdev **dev)
 1128 {
 1129         struct cdev *pdev;
 1130         struct snddev_info *pcm_dev;
 1131         struct snddev_channel *pcm_chan;
 1132         int i, unit, devtype;
 1133         int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
 1134         char *devnames[3] = {"dsp", "dspW", "audio"};
 1135 
 1136         if (*dev != NULL)
 1137                 return;
 1138         if (pcm_devclass == NULL)
 1139                 return;
 1140 
 1141         devtype = 0;
 1142         unit = -1;
 1143         for (i = 0; (i < 3) && (unit == -1); i++) {
 1144                 devtype = devtypes[i];
 1145                 if (strcmp(name, devnames[i]) == 0) {
 1146                         unit = snd_unit;
 1147                 } else {
 1148                         if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
 1149                                 unit = -1;
 1150                 }
 1151         }
 1152         if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
 1153                 return;
 1154 
 1155         pcm_dev = devclass_get_softc(pcm_devclass, unit);
 1156 
 1157         if (pcm_dev == NULL)
 1158                 return;
 1159 
 1160         SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) {
 1161 
 1162                 switch(devtype) {
 1163                         case SND_DEV_DSP:
 1164                                 pdev = pcm_chan->dsp_devt;
 1165                                 break;
 1166                         case SND_DEV_DSP16:
 1167                                 pdev = pcm_chan->dspW_devt;
 1168                                 break;
 1169                         case SND_DEV_AUDIO:
 1170                                 pdev = pcm_chan->audio_devt;
 1171                                 break;
 1172                         default:
 1173                                 panic("Unknown devtype %d", devtype);
 1174                 }
 1175 
 1176                 if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
 1177                         *dev = pdev;
 1178                         return;
 1179                 }
 1180         }
 1181 }
 1182 
 1183 static void
 1184 dsp_sysinit(void *p)
 1185 {
 1186         dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
 1187 }
 1188 
 1189 static void
 1190 dsp_sysuninit(void *p)
 1191 {
 1192         if (dsp_ehtag != NULL)
 1193                 EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
 1194 }
 1195 
 1196 SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
 1197 SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
 1198 #endif
 1199 
 1200 

Cache object: de17866483527d6dd81989a4bccfc0cb


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