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/vchan.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2006-2009 Ariff Abdullah <ariff@FreeBSD.org>
    3  * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 /* Almost entirely rewritten to add multi-format/channels mixing support. */
   29 
   30 #ifdef HAVE_KERNEL_OPTION_HEADERS
   31 #include "opt_snd.h"
   32 #endif
   33 
   34 #include <dev/sound/pcm/sound.h>
   35 #include <dev/sound/pcm/vchan.h>
   36 
   37 SND_DECLARE_FILE("$FreeBSD: releng/8.1/sys/dev/sound/pcm/vchan.c 193640 2009-06-07 19:12:08Z ariff $");
   38 
   39 /*
   40  * [ac3 , dts , linear , 0, linear, 0]
   41  */
   42 #define FMTLIST_MAX             6
   43 #define FMTLIST_OFFSET          4
   44 #define DIGFMTS_MAX             2
   45 
   46 #ifdef SND_DEBUG
   47 static int snd_passthrough_verbose = 0;
   48 SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RW,
   49         &snd_passthrough_verbose, 0, "passthrough verbosity");
   50 
   51 #endif
   52 
   53 struct vchan_info {
   54         struct pcm_channel *channel;
   55         struct pcmchan_caps caps;
   56         uint32_t fmtlist[FMTLIST_MAX];
   57         int trigger;
   58 };
   59 
   60 static void *
   61 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
   62     struct pcm_channel *c, int dir)
   63 {
   64         struct vchan_info *info;
   65         struct pcm_channel *p;
   66         uint32_t i, j, *fmtlist;
   67 
   68         KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC,
   69             ("vchan_init: bad direction"));
   70         KASSERT(c != NULL && c->parentchannel != NULL,
   71             ("vchan_init: bad channels"));
   72 
   73         info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO);
   74         info->channel = c;
   75         info->trigger = PCMTRIG_STOP;
   76         p = c->parentchannel;
   77 
   78         CHN_LOCK(p);
   79 
   80         fmtlist = chn_getcaps(p)->fmtlist;
   81         for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) {
   82                 if (fmtlist[i] & AFMT_PASSTHROUGH)
   83                         info->fmtlist[j++] = fmtlist[i];
   84         }
   85         if (p->format & AFMT_VCHAN)
   86                 info->fmtlist[j] = p->format;
   87         else
   88                 info->fmtlist[j] = VCHAN_DEFAULT_FORMAT;
   89         info->caps.fmtlist = info->fmtlist +
   90             ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET);
   91 
   92         CHN_UNLOCK(p);
   93 
   94         c->flags |= CHN_F_VIRTUAL;
   95 
   96         return (info);
   97 }
   98 
   99 static int
  100 vchan_free(kobj_t obj, void *data)
  101 {
  102 
  103         free(data, M_DEVBUF);
  104 
  105         return (0);
  106 }
  107 
  108 static int
  109 vchan_setformat(kobj_t obj, void *data, uint32_t format)
  110 {
  111         struct vchan_info *info;
  112 
  113         info = data;
  114 
  115         CHN_LOCKASSERT(info->channel);
  116 
  117         if (!snd_fmtvalid(format, info->caps.fmtlist))
  118                 return (-1);
  119 
  120         return (0);
  121 }
  122 
  123 static uint32_t
  124 vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
  125 {
  126         struct vchan_info *info;
  127 
  128         info = data;
  129 
  130         CHN_LOCKASSERT(info->channel);
  131 
  132         return (info->caps.maxspeed);
  133 }
  134 
  135 static int
  136 vchan_trigger(kobj_t obj, void *data, int go)
  137 {
  138         struct vchan_info *info;
  139         struct pcm_channel *c, *p;
  140         int ret, otrigger;
  141 
  142         info = data;
  143 
  144         if (!PCMTRIG_COMMON(go) || go == info->trigger)
  145                 return (0);
  146 
  147         c = info->channel;
  148         p = c->parentchannel;
  149         otrigger = info->trigger;
  150         info->trigger = go;
  151 
  152         CHN_LOCKASSERT(c);
  153 
  154         CHN_UNLOCK(c);
  155         CHN_LOCK(p);
  156 
  157         switch (go) {
  158         case PCMTRIG_START:
  159                 if (otrigger != PCMTRIG_START)
  160                         CHN_INSERT_HEAD(p, c, children.busy);
  161                 break;
  162         case PCMTRIG_STOP:
  163         case PCMTRIG_ABORT:
  164                 if (otrigger == PCMTRIG_START)
  165                         CHN_REMOVE(p, c, children.busy);
  166                 break;
  167         default:
  168                 break;
  169         }
  170 
  171         ret = chn_notify(p, CHN_N_TRIGGER);
  172 
  173         CHN_LOCK(c);
  174 
  175         if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c))
  176                 ret = vchan_sync(c);
  177 
  178         CHN_UNLOCK(c);
  179         CHN_UNLOCK(p);
  180         CHN_LOCK(c);
  181 
  182         return (ret);
  183 }
  184 
  185 static struct pcmchan_caps *
  186 vchan_getcaps(kobj_t obj, void *data)
  187 {
  188         struct vchan_info *info;
  189         struct pcm_channel *c;
  190         uint32_t pformat, pspeed, pflags, i;
  191 
  192         info = data;
  193         c = info->channel;
  194         pformat = c->parentchannel->format;
  195         pspeed = c->parentchannel->speed;
  196         pflags = c->parentchannel->flags;
  197 
  198         CHN_LOCKASSERT(c);
  199 
  200         if (pflags & CHN_F_VCHAN_DYNAMIC) {
  201                 info->caps.fmtlist = info->fmtlist;
  202                 if (pformat & AFMT_VCHAN) {
  203                         for (i = 0; info->caps.fmtlist[i] != 0; i++) {
  204                                 if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH)
  205                                         continue;
  206                                 break;
  207                         }
  208                         info->caps.fmtlist[i] = pformat;
  209                 }
  210                 if (c->format & AFMT_PASSTHROUGH)
  211                         info->caps.minspeed = c->speed;
  212                 else 
  213                         info->caps.minspeed = pspeed;
  214                 info->caps.maxspeed = info->caps.minspeed;
  215         } else {
  216                 info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET;
  217                 if (pformat & AFMT_VCHAN)
  218                         info->caps.fmtlist[0] = pformat;
  219                 else {
  220                         device_printf(c->dev,
  221                             "%s(): invalid vchan format 0x%08x",
  222                             __func__, pformat);
  223                         info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT;
  224                 }
  225                 info->caps.minspeed = pspeed;
  226                 info->caps.maxspeed = info->caps.minspeed;
  227         }
  228 
  229         return (&info->caps);
  230 }
  231 
  232 static struct pcmchan_matrix *
  233 vchan_getmatrix(kobj_t obj, void *data, uint32_t format)
  234 {
  235 
  236         return (feeder_matrix_format_map(format));
  237 }
  238 
  239 static kobj_method_t vchan_methods[] = {
  240         KOBJMETHOD(channel_init,                vchan_init),
  241         KOBJMETHOD(channel_free,                vchan_free),
  242         KOBJMETHOD(channel_setformat,           vchan_setformat),
  243         KOBJMETHOD(channel_setspeed,            vchan_setspeed),
  244         KOBJMETHOD(channel_trigger,             vchan_trigger),
  245         KOBJMETHOD(channel_getcaps,             vchan_getcaps),
  246         KOBJMETHOD(channel_getmatrix,           vchan_getmatrix),
  247         KOBJMETHOD_END
  248 };
  249 CHANNEL_DECLARE(vchan);
  250 
  251 static void
  252 pcm_getparentchannel(struct snddev_info *d,
  253     struct pcm_channel **wrch, struct pcm_channel **rdch)
  254 {
  255         struct pcm_channel **ch, *wch, *rch, *c;
  256 
  257         KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__));
  258 
  259         PCM_BUSYASSERT(d);
  260         PCM_UNLOCKASSERT(d);
  261 
  262         wch = NULL;
  263         rch = NULL;
  264 
  265         CHN_FOREACH(c, d, channels.pcm) {
  266                 CHN_LOCK(c);
  267                 ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch;
  268                 if (c->flags & CHN_F_VIRTUAL) {
  269                         /* Sanity check */
  270                         if (*ch != NULL && *ch != c->parentchannel) {
  271                                 CHN_UNLOCK(c);
  272                                 *ch = NULL;
  273                                 break;
  274                         }
  275                 } else if (c->flags & CHN_F_HAS_VCHAN) {
  276                         /* No way!! */
  277                         if (*ch != NULL) {
  278                                 CHN_UNLOCK(c);
  279                                 *ch = NULL;
  280                                 break;
  281                         }
  282                         *ch = c;
  283                 }
  284                 CHN_UNLOCK(c);
  285         }
  286 
  287         if (wrch != NULL)
  288                 *wrch = wch;
  289         if (rdch != NULL)
  290                 *rdch = rch;
  291 }
  292 
  293 static int
  294 sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
  295 {
  296         struct snddev_info *d;
  297         int direction, vchancount;
  298         int err, cnt;
  299 
  300         d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
  301         if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
  302                 return (EINVAL);
  303 
  304         PCM_LOCK(d);
  305         PCM_WAIT(d);
  306 
  307         switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
  308         case VCHAN_PLAY:
  309                 direction = PCMDIR_PLAY;
  310                 vchancount = d->pvchancount;
  311                 cnt = d->playcount;
  312                 break;
  313         case VCHAN_REC:
  314                 direction = PCMDIR_REC;
  315                 vchancount = d->rvchancount;
  316                 cnt = d->reccount;
  317                 break;
  318         default:
  319                 PCM_UNLOCK(d);
  320                 return (EINVAL);
  321                 break;
  322         }
  323 
  324         if (cnt < 1) {
  325                 PCM_UNLOCK(d);
  326                 return (ENODEV);
  327         }
  328 
  329         PCM_ACQUIRE(d);
  330         PCM_UNLOCK(d);
  331 
  332         cnt = vchancount;
  333         err = sysctl_handle_int(oidp, &cnt, 0, req);
  334 
  335         if (err == 0 && req->newptr != NULL && vchancount != cnt) {
  336                 if (cnt < 0)
  337                         cnt = 0;
  338                 if (cnt > SND_MAXVCHANS)
  339                         cnt = SND_MAXVCHANS;
  340                 err = pcm_setvchans(d, direction, cnt, -1);
  341         }
  342 
  343         PCM_RELEASE_QUICK(d);
  344 
  345         return err;
  346 }
  347 
  348 static int
  349 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
  350 {
  351         struct snddev_info *d;
  352         struct pcm_channel *c;
  353         uint32_t dflags;
  354         int direction, ret;
  355         char dtype[16];
  356 
  357         d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
  358         if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
  359                 return (EINVAL);
  360 
  361         PCM_LOCK(d);
  362         PCM_WAIT(d);
  363 
  364         switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
  365         case VCHAN_PLAY:
  366                 direction = PCMDIR_PLAY;
  367                 break;
  368         case VCHAN_REC:
  369                 direction = PCMDIR_REC;
  370                 break;
  371         default:
  372                 PCM_UNLOCK(d);
  373                 return (EINVAL);
  374                 break;
  375         }
  376 
  377         PCM_ACQUIRE(d);
  378         PCM_UNLOCK(d);
  379 
  380         if (direction == PCMDIR_PLAY)
  381                 pcm_getparentchannel(d, &c, NULL);
  382         else
  383                 pcm_getparentchannel(d, NULL, &c);
  384 
  385         if (c == NULL) {
  386                 PCM_RELEASE_QUICK(d);
  387                 return (EINVAL);
  388         }
  389 
  390         KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
  391             __func__, direction, c->direction));
  392 
  393         CHN_LOCK(c);
  394         if (c->flags & CHN_F_VCHAN_PASSTHROUGH)
  395                 strlcpy(dtype, "passthrough", sizeof(dtype));
  396         else if (c->flags & CHN_F_VCHAN_ADAPTIVE)
  397                 strlcpy(dtype, "adaptive", sizeof(dtype));
  398         else
  399                 strlcpy(dtype, "fixed", sizeof(dtype));
  400         CHN_UNLOCK(c);
  401 
  402         ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req);
  403         if (ret == 0 && req->newptr != NULL) {
  404                 if (strcasecmp(dtype, "passthrough") == 0 ||
  405                     strcmp(dtype, "1") == 0)
  406                         dflags = CHN_F_VCHAN_PASSTHROUGH;
  407                 else if (strcasecmp(dtype, "adaptive") == 0 ||
  408                     strcmp(dtype, "2") == 0)
  409                         dflags = CHN_F_VCHAN_ADAPTIVE;
  410                 else if (strcasecmp(dtype, "fixed") == 0 ||
  411                     strcmp(dtype, "") == 0)
  412                         dflags = 0;
  413                 else {
  414                         PCM_RELEASE_QUICK(d);
  415                         return (EINVAL);
  416                 }
  417                 CHN_LOCK(c);
  418                 if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) ||
  419                     (c->flags & CHN_F_PASSTHROUGH)) {
  420                         CHN_UNLOCK(c);
  421                         PCM_RELEASE_QUICK(d);
  422                         return (0);
  423                 }
  424                 c->flags &= ~CHN_F_VCHAN_DYNAMIC;
  425                 c->flags |= dflags;
  426                 CHN_UNLOCK(c);
  427         }
  428 
  429         PCM_RELEASE_QUICK(d);
  430 
  431         return (ret);
  432 }
  433 
  434 /* 
  435  * On the fly vchan rate/format settings
  436  */
  437 
  438 #define VCHAN_ACCESSIBLE(c)     (!((c)->flags & (CHN_F_PASSTHROUGH |    \
  439                                  CHN_F_EXCLUSIVE)) &&                   \
  440                                  (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \
  441                                  CHN_STOPPED(c)))
  442 static int
  443 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
  444 {
  445         struct snddev_info *d;
  446         struct pcm_channel *c, *ch;
  447         struct pcmchan_caps *caps;
  448         int *vchanrate, vchancount, direction, ret, newspd, restart;
  449 
  450         d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
  451         if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
  452                 return (EINVAL);
  453 
  454         PCM_LOCK(d);
  455         PCM_WAIT(d);
  456 
  457         switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
  458         case VCHAN_PLAY:
  459                 direction = PCMDIR_PLAY;
  460                 vchancount = d->pvchancount;
  461                 vchanrate = &d->pvchanrate;
  462                 break;
  463         case VCHAN_REC:
  464                 direction = PCMDIR_REC;
  465                 vchancount = d->rvchancount;
  466                 vchanrate = &d->rvchanrate;
  467                 break;
  468         default:
  469                 PCM_UNLOCK(d);
  470                 return (EINVAL);
  471                 break;
  472         }
  473 
  474         if (vchancount < 1) {
  475                 PCM_UNLOCK(d);
  476                 return (EINVAL);
  477         }
  478 
  479         PCM_ACQUIRE(d);
  480         PCM_UNLOCK(d);
  481 
  482         if (direction == PCMDIR_PLAY)
  483                 pcm_getparentchannel(d, &c, NULL);
  484         else
  485                 pcm_getparentchannel(d, NULL, &c);
  486 
  487         if (c == NULL) {
  488                 PCM_RELEASE_QUICK(d);
  489                 return (EINVAL);
  490         }
  491 
  492         KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
  493             __func__, direction, c->direction));
  494 
  495         CHN_LOCK(c);
  496         newspd = c->speed;
  497         CHN_UNLOCK(c);
  498 
  499         ret = sysctl_handle_int(oidp, &newspd, 0, req);
  500         if (ret != 0 || req->newptr == NULL) {
  501                 PCM_RELEASE_QUICK(d);
  502                 return (ret);
  503         }
  504 
  505         if (newspd < 1 || newspd < feeder_rate_min ||
  506             newspd > feeder_rate_max) {
  507                 PCM_RELEASE_QUICK(d);
  508                 return (EINVAL);
  509         }
  510 
  511         CHN_LOCK(c);
  512 
  513         if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) {
  514                 if (CHN_STARTED(c)) {
  515                         chn_abort(c);
  516                         restart = 1;
  517                 } else
  518                         restart = 0;
  519 
  520                 if (feeder_rate_round) {
  521                         caps = chn_getcaps(c);
  522                         RANGE(newspd, caps->minspeed, caps->maxspeed);
  523                         newspd = CHANNEL_SETSPEED(c->methods,
  524                             c->devinfo, newspd);
  525                 }
  526 
  527                 ret = chn_reset(c, c->format, newspd);
  528                 if (ret == 0) {
  529                         *vchanrate = c->speed;
  530                         if (restart != 0) {
  531                                 CHN_FOREACH(ch, c, children.busy) {
  532                                         CHN_LOCK(ch);
  533                                         if (VCHAN_SYNC_REQUIRED(ch))
  534                                                 vchan_sync(ch);
  535                                         CHN_UNLOCK(ch);
  536                                 }
  537                                 c->flags |= CHN_F_DIRTY;
  538                                 ret = chn_start(c, 1);
  539                         }
  540                 }
  541         }
  542 
  543         CHN_UNLOCK(c);
  544 
  545         PCM_RELEASE_QUICK(d);
  546 
  547         return (ret);
  548 }
  549 
  550 static int
  551 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
  552 {
  553         struct snddev_info *d;
  554         struct pcm_channel *c, *ch;
  555         uint32_t newfmt;
  556         int *vchanformat, vchancount, direction, ret, restart;
  557         char fmtstr[AFMTSTR_LEN];
  558 
  559         d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
  560         if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
  561                 return (EINVAL);
  562 
  563         PCM_LOCK(d);
  564         PCM_WAIT(d);
  565 
  566         switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
  567         case VCHAN_PLAY:
  568                 direction = PCMDIR_PLAY;
  569                 vchancount = d->pvchancount;
  570                 vchanformat = &d->pvchanformat;
  571                 break;
  572         case VCHAN_REC:
  573                 direction = PCMDIR_REC;
  574                 vchancount = d->rvchancount;
  575                 vchanformat = &d->rvchanformat;
  576                 break;
  577         default:
  578                 PCM_UNLOCK(d);
  579                 return (EINVAL);
  580                 break;
  581         }
  582 
  583         if (vchancount < 1) {
  584                 PCM_UNLOCK(d);
  585                 return (EINVAL);
  586         }
  587 
  588         PCM_ACQUIRE(d);
  589         PCM_UNLOCK(d);
  590 
  591         if (direction == PCMDIR_PLAY)
  592                 pcm_getparentchannel(d, &c, NULL);
  593         else
  594                 pcm_getparentchannel(d, NULL, &c);
  595 
  596         if (c == NULL) {
  597                 PCM_RELEASE_QUICK(d);
  598                 return (EINVAL);
  599         }
  600 
  601         KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
  602             __func__, direction, c->direction));
  603 
  604         CHN_LOCK(c);
  605 
  606         bzero(fmtstr, sizeof(fmtstr));
  607 
  608         if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format)
  609                 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr));
  610 
  611         CHN_UNLOCK(c);
  612 
  613         ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
  614         if (ret != 0 || req->newptr == NULL) {
  615                 PCM_RELEASE_QUICK(d);
  616                 return (ret);
  617         }
  618 
  619         newfmt = snd_str2afmt(fmtstr);
  620         if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) {
  621                 PCM_RELEASE_QUICK(d);
  622                 return (EINVAL);
  623         }
  624 
  625         CHN_LOCK(c);
  626 
  627         if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) {
  628                 if (CHN_STARTED(c)) {
  629                         chn_abort(c);
  630                         restart = 1;
  631                 } else
  632                         restart = 0;
  633 
  634                 ret = chn_reset(c, newfmt, c->speed);
  635                 if (ret == 0) {
  636                         *vchanformat = c->format;
  637                         if (restart != 0) {
  638                                 CHN_FOREACH(ch, c, children.busy) {
  639                                         CHN_LOCK(ch);
  640                                         if (VCHAN_SYNC_REQUIRED(ch))
  641                                                 vchan_sync(ch);
  642                                         CHN_UNLOCK(ch);
  643                                 }
  644                                 c->flags |= CHN_F_DIRTY;
  645                                 ret = chn_start(c, 1);
  646                         }
  647                 }
  648         }
  649 
  650         CHN_UNLOCK(c);
  651 
  652         PCM_RELEASE_QUICK(d);
  653 
  654         return (ret);
  655 }
  656 
  657 /* virtual channel interface */
  658 
  659 #define VCHAN_FMT_HINT(x)       ((x) == PCMDIR_PLAY_VIRTUAL) ?          \
  660                                 "play.vchanformat" : "rec.vchanformat"
  661 #define VCHAN_SPD_HINT(x)       ((x) == PCMDIR_PLAY_VIRTUAL) ?          \
  662                                 "play.vchanrate" : "rec.vchanrate"
  663 
  664 int
  665 vchan_create(struct pcm_channel *parent, int num)
  666 {
  667         struct snddev_info *d;
  668         struct pcm_channel *ch;
  669         struct pcmchan_caps *parent_caps;
  670         uint32_t vchanfmt, vchanspd;
  671         int ret, direction, r, save;
  672 
  673         d = parent->parentsnddev;
  674 
  675         PCM_BUSYASSERT(d);
  676         CHN_LOCKASSERT(parent);
  677 
  678         if (!(parent->flags & CHN_F_BUSY))
  679                 return (EBUSY);
  680 
  681         if (!(parent->direction == PCMDIR_PLAY ||
  682             parent->direction == PCMDIR_REC))
  683                 return (EINVAL);
  684 
  685         d = parent->parentsnddev;
  686 
  687         CHN_UNLOCK(parent);
  688         PCM_LOCK(d);
  689 
  690         if (parent->direction == PCMDIR_PLAY) {
  691                 direction = PCMDIR_PLAY_VIRTUAL;
  692                 vchanfmt = d->pvchanformat;
  693                 vchanspd = d->pvchanrate;
  694         } else {
  695                 direction = PCMDIR_REC_VIRTUAL;
  696                 vchanfmt = d->rvchanformat;
  697                 vchanspd = d->rvchanrate;
  698         }
  699 
  700         /* create a new playback channel */
  701         ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent);
  702         if (ch == NULL) {
  703                 PCM_UNLOCK(d);
  704                 CHN_LOCK(parent);
  705                 return (ENODEV);
  706         }
  707 
  708         /* add us to our grandparent's channel list */
  709         ret = pcm_chn_add(d, ch);
  710         PCM_UNLOCK(d);
  711         if (ret != 0) {
  712                 pcm_chn_destroy(ch);
  713                 CHN_LOCK(parent);
  714                 return (ret);
  715         }
  716 
  717         CHN_LOCK(parent);
  718         /*
  719          * Add us to our parent channel's children in reverse order
  720          * so future destruction will pick the last (biggest number)
  721          * channel.
  722          */
  723         CHN_INSERT_SORT_DESCEND(parent, ch, children);
  724 
  725         if (parent->flags & CHN_F_HAS_VCHAN)
  726                 return (0);
  727 
  728         parent->flags |= CHN_F_HAS_VCHAN;
  729 
  730         parent_caps = chn_getcaps(parent);
  731         if (parent_caps == NULL)
  732                 ret = EINVAL;
  733 
  734         save = 0;
  735 
  736         if (ret == 0 && vchanfmt == 0) {
  737                 const char *vfmt;
  738 
  739                 CHN_UNLOCK(parent);
  740                 r = resource_string_value(device_get_name(parent->dev),
  741                     device_get_unit(parent->dev), VCHAN_FMT_HINT(direction),
  742                     &vfmt);
  743                 CHN_LOCK(parent);
  744                 if (r != 0)
  745                         vfmt = NULL;
  746                 if (vfmt != NULL) {
  747                         vchanfmt = snd_str2afmt(vfmt);
  748                         if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN))
  749                                 vchanfmt = 0;
  750                 }
  751                 if (vchanfmt == 0)
  752                         vchanfmt = VCHAN_DEFAULT_FORMAT;
  753                 save = 1;
  754         }
  755 
  756         if (ret == 0 && vchanspd == 0) {
  757                 /*
  758                  * This is very sad. Few soundcards advertised as being
  759                  * able to do (insanely) higher/lower speed, but in
  760                  * reality, they simply can't. At least, we give user chance
  761                  * to set sane value via kernel hints or sysctl.
  762                  */
  763                 CHN_UNLOCK(parent);
  764                 r = resource_int_value(device_get_name(parent->dev),
  765                     device_get_unit(parent->dev), VCHAN_SPD_HINT(direction),
  766                     &vchanspd);
  767                 CHN_LOCK(parent);
  768                 if (r != 0) {
  769                         /*
  770                          * No saved value, no hint, NOTHING.
  771                          *
  772                          * Workaround for sb16 running
  773                          * poorly at 45k / 49k.
  774                          */
  775                         switch (parent_caps->maxspeed) {
  776                         case 45000:
  777                         case 49000:
  778                                 vchanspd = 44100;
  779                                 break;
  780                         default:
  781                                 vchanspd = VCHAN_DEFAULT_RATE;
  782                                 if (vchanspd > parent_caps->maxspeed)
  783                                         vchanspd = parent_caps->maxspeed;
  784                                 break;
  785                         }
  786                         if (vchanspd < parent_caps->minspeed)
  787                                 vchanspd = parent_caps->minspeed;
  788                 }
  789                 save = 1;
  790         }
  791 
  792         if (ret == 0) {
  793                 /*
  794                  * Limit the speed between feeder_rate_min <-> feeder_rate_max.
  795                  */
  796                 if (vchanspd < feeder_rate_min)
  797                         vchanspd = feeder_rate_min;
  798                 if (vchanspd > feeder_rate_max)
  799                         vchanspd = feeder_rate_max;
  800 
  801                 if (feeder_rate_round) {
  802                         RANGE(vchanspd, parent_caps->minspeed,
  803                             parent_caps->maxspeed);
  804                         vchanspd = CHANNEL_SETSPEED(parent->methods,
  805                             parent->devinfo, vchanspd);
  806                 }
  807 
  808                 ret = chn_reset(parent, vchanfmt, vchanspd);
  809         }
  810 
  811         if (ret == 0 && save) {
  812                 /*
  813                  * Save new value.
  814                  */
  815                 if (direction == PCMDIR_PLAY_VIRTUAL) {
  816                         d->pvchanformat = parent->format;
  817                         d->pvchanrate = parent->speed;
  818                 } else {
  819                         d->rvchanformat = parent->format;
  820                         d->rvchanrate = parent->speed;
  821                 }
  822         }
  823         
  824         /*
  825          * If the parent channel supports digital format,
  826          * enable passthrough mode.
  827          */
  828         if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
  829                 parent->flags &= ~CHN_F_VCHAN_DYNAMIC;
  830                 parent->flags |= CHN_F_VCHAN_PASSTHROUGH;
  831         }
  832 
  833         if (ret != 0) {
  834                 CHN_REMOVE(parent, ch, children);
  835                 parent->flags &= ~CHN_F_HAS_VCHAN;
  836                 CHN_UNLOCK(parent);
  837                 PCM_LOCK(d);
  838                 if (pcm_chn_remove(d, ch) == 0) {
  839                         PCM_UNLOCK(d);
  840                         pcm_chn_destroy(ch);
  841                 } else
  842                         PCM_UNLOCK(d);
  843                 CHN_LOCK(parent);
  844         }
  845 
  846         return (ret);
  847 }
  848 
  849 int
  850 vchan_destroy(struct pcm_channel *c)
  851 {
  852         struct pcm_channel *parent;
  853         struct snddev_info *d;
  854         int ret;
  855 
  856         KASSERT(c != NULL && c->parentchannel != NULL &&
  857             c->parentsnddev != NULL, ("%s(): invalid channel=%p",
  858             __func__, c));
  859 
  860         CHN_LOCKASSERT(c);
  861 
  862         d = c->parentsnddev;
  863         parent = c->parentchannel;
  864 
  865         PCM_BUSYASSERT(d);
  866         CHN_LOCKASSERT(parent);
  867 
  868         CHN_UNLOCK(c);
  869 
  870         if (!(parent->flags & CHN_F_BUSY))
  871                 return (EBUSY);
  872 
  873         if (CHN_EMPTY(parent, children))
  874                 return (EINVAL);
  875 
  876         /* remove us from our parent's children list */
  877         CHN_REMOVE(parent, c, children);
  878 
  879         if (CHN_EMPTY(parent, children)) {
  880                 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
  881                 chn_reset(parent, parent->format, parent->speed);
  882         }
  883 
  884         CHN_UNLOCK(parent);
  885 
  886         /* remove us from our grandparent's channel list */
  887         PCM_LOCK(d);
  888         ret = pcm_chn_remove(d, c);
  889         PCM_UNLOCK(d);
  890 
  891         /* destroy ourselves */
  892         if (ret == 0)
  893                 ret = pcm_chn_destroy(c);
  894 
  895         CHN_LOCK(parent);
  896 
  897         return (ret);
  898 }
  899 
  900 int
  901 #ifdef SND_DEBUG
  902 vchan_passthrough(struct pcm_channel *c, const char *caller)
  903 #else
  904 vchan_sync(struct pcm_channel *c)
  905 #endif
  906 {
  907         int ret;
  908 
  909         KASSERT(c != NULL && c->parentchannel != NULL &&
  910             (c->flags & CHN_F_VIRTUAL),
  911             ("%s(): invalid passthrough", __func__));
  912         CHN_LOCKASSERT(c);
  913         CHN_LOCKASSERT(c->parentchannel);
  914 
  915         sndbuf_setspd(c->bufhard, c->parentchannel->speed);
  916         c->flags |= CHN_F_PASSTHROUGH;
  917         ret = feeder_chain(c);
  918         c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH);
  919         if (ret != 0)
  920                 c->flags |= CHN_F_DIRTY;
  921 
  922 #ifdef SND_DEBUG
  923         if (snd_passthrough_verbose != 0) {
  924                 char *devname, buf[CHN_NAMELEN];
  925 
  926                 devname = dsp_unit2name(buf, sizeof(buf), c->unit);
  927                 device_printf(c->dev,
  928                     "%s(%s/%s) %s() -> re-sync err=%d\n",
  929                     __func__, (devname != NULL) ? devname : "dspX", c->comm,
  930                     caller, ret);
  931         }
  932 #endif
  933 
  934         return (ret);
  935 }
  936 
  937 void
  938 vchan_initsys(device_t dev)
  939 {
  940         struct snddev_info *d;
  941         int unit;
  942 
  943         unit = device_get_unit(dev);
  944         d = device_get_softc(dev);
  945 
  946         /* Play */
  947         SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
  948             SYSCTL_CHILDREN(d->play_sysctl_tree),
  949             OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW,
  950             VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
  951             sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
  952         SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
  953             SYSCTL_CHILDREN(d->play_sysctl_tree),
  954             OID_AUTO, "vchanmode", CTLTYPE_STRING | CTLFLAG_RW,
  955             VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
  956             sysctl_dev_pcm_vchanmode, "A",
  957             "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
  958         SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
  959             SYSCTL_CHILDREN(d->play_sysctl_tree),
  960             OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW,
  961             VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
  962             sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
  963         SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
  964             SYSCTL_CHILDREN(d->play_sysctl_tree),
  965             OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW,
  966             VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
  967             sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
  968         /* Rec */
  969         SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
  970             SYSCTL_CHILDREN(d->rec_sysctl_tree),
  971             OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW,
  972             VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
  973             sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
  974         SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
  975             SYSCTL_CHILDREN(d->rec_sysctl_tree),
  976             OID_AUTO, "vchanmode", CTLTYPE_STRING | CTLFLAG_RW,
  977             VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
  978             sysctl_dev_pcm_vchanmode, "A",
  979             "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
  980         SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
  981             SYSCTL_CHILDREN(d->rec_sysctl_tree),
  982             OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW,
  983             VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
  984             sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
  985         SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
  986             SYSCTL_CHILDREN(d->rec_sysctl_tree),
  987             OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW,
  988             VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
  989             sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
  990 }

Cache object: ca23767e1c1c53a539f830977407605e


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