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

Cache object: 496411e4e8afdab78536d63a958d9f24


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