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

Cache object: 03dcb054c60348235ebbfea5c50f06de


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