Date: Tue, 25 Feb 2025 11:48:14 GMT From: Christos Margiolis <christos@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 02d4eeabfd73 - main - sound: Allocate vchans on-demand Message-ID: <202502251148.51PBmEaZ050898@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by christos: URL: https://cgit.FreeBSD.org/src/commit/?id=02d4eeabfd73e6a827f5d42601e99aad92060b04 commit 02d4eeabfd73e6a827f5d42601e99aad92060b04 Author: Christos Margiolis <christos@FreeBSD.org> AuthorDate: 2025-02-25 11:43:39 +0000 Commit: Christos Margiolis <christos@FreeBSD.org> CommitDate: 2025-02-25 11:43:39 +0000 sound: Allocate vchans on-demand Refactor pcm_chnalloc() and merge with parts of vchan_setnew() (now removed) and dsp_open()’s channel creation into a new dsp_chn_alloc() function. The function is responsible for either using a free HW channel (if vchans are disabled), or allocating a new vchan. Clean up allocated vchans associated with a given dsp_cdevpriv on dsp_close() instead of leaving them unused. hw.snd.vchans_enable (previously hw.snd.maxautovchans) and dev.pcm.X.{play|rec}.vchans now work as tunables to only enable/disable vchans, as opposed to setting their number and/or (de-)allocating vchans. Since these sysctls do not trigger any (de-)allocations anymore, their effect is instantaneous, whereas before we could have frozen the machine (when trying to allocate new vchans) when setting dev.pcm.X.{play|rec}.vchans to a very large value. Create a new "primary" channel sublist so that we do not waste time looping through all channels in dsp_chn_alloc(), since we are only looking for a parent channel to either use, or add a new vchan to. This guarantees a steady traversal speed, as the parent channels are most likely going to be just a handful (2). What was currently in place was a loop through the whole channel list, which meant that the traversal would take longer the more channels were added to that list. Sponsored by: The FreeBSD Foundation MFC after: 1 week Reviewed by: dev_submerge.ch Differential Revision: https://reviews.freebsd.org/D47917 --- share/man/man4/pcm.4 | 23 ++-- sys/dev/sound/pcm/channel.c | 14 ++- sys/dev/sound/pcm/channel.h | 3 + sys/dev/sound/pcm/dsp.c | 233 ++++++++++++++++++++---------------- sys/dev/sound/pcm/sound.c | 70 ++--------- sys/dev/sound/pcm/sound.h | 13 +- sys/dev/sound/pcm/vchan.c | 285 +++++++++++++------------------------------- sys/dev/sound/pcm/vchan.h | 7 +- 8 files changed, 254 insertions(+), 394 deletions(-) diff --git a/share/man/man4/pcm.4 b/share/man/man4/pcm.4 index 769f562fe91e..e25ffe264ae8 100644 --- a/share/man/man4/pcm.4 +++ b/share/man/man4/pcm.4 @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd March 24, 2024 +.Dd December 4, 2024 .Dt SOUND 4 .Os .Sh NAME @@ -360,14 +360,12 @@ A value of 0 will use a low and aggressive latency profile which can result in possible underruns if the application cannot keep up with a rapid irq rate, especially during high workload. The default value is 1, which is considered a moderate/safe latency profile. -.It Va hw.snd.maxautovchans -Global VCHAN setting that only affects devices with at least one playback or -recording channel available. -The sound system will dynamically create up to this many VCHANs. -Set to -.Dq 0 -if no VCHANs are desired. -Maximum value is 256. +.It Va hw.snd.vchans_enable +Global VCHAN setting to enable (1) or disable (0) VCHANs. +This setting can be overridden for an individual device by using the +.Va dev.pcm.%d.[play|rec].vchans +tunables. +Default is enabled. .It Va hw.snd.report_soft_formats Controls the internal format conversion if it is available transparently to the application software. @@ -434,11 +432,8 @@ The recommended way to use bitperfect mode is to disable VCHANs and enable this sysctl. Default is disabled. .It Va dev.pcm.%d.[play|rec].vchans -The current number of VCHANs allocated per device. -This can be set to preallocate a certain number of VCHANs. -Setting this value to -.Dq 0 -will disable VCHANs for this device. +Enable (1) or disable (0) VCHANs. +Default is enabled. .It Va dev.pcm.%d.[play|rec].vchanformat Format for VCHAN mixing. All playback paths will be converted to this format before the mixing diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c index 58315610312e..17c11dc33b7a 100644 --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -1172,7 +1172,7 @@ chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, struct feeder_class *fc; struct snd_dbuf *b, *bs; char buf[CHN_NAMELEN]; - int i, direction; + int err, i, direction; PCM_BUSYASSERT(d); PCM_LOCKASSERT(d); @@ -1279,8 +1279,18 @@ chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_WAITOK); } + if ((c->flags & CHN_F_VIRTUAL) == 0) { + CHN_LOCK(c); + err = chn_reset(c, c->format, c->speed); + CHN_UNLOCK(c); + if (err != 0) + goto fail; + } + PCM_LOCK(d); CHN_INSERT_SORT_ASCEND(d, c, channels.pcm); + if ((c->flags & CHN_F_VIRTUAL) == 0) + CHN_INSERT_SORT_ASCEND(d, c, channels.pcm.primary); switch (c->type) { case PCMDIR_PLAY: @@ -1332,6 +1342,8 @@ chn_kill(struct pcm_channel *c) PCM_LOCK(d); CHN_REMOVE(d, c, channels.pcm); + if ((c->flags & CHN_F_VIRTUAL) == 0) + CHN_REMOVE(d, c, channels.pcm.primary); switch (c->type) { case PCMDIR_PLAY: diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h index 6eaad8cc2c0b..31c617a6df78 100644 --- a/sys/dev/sound/pcm/channel.h +++ b/sys/dev/sound/pcm/channel.h @@ -160,6 +160,9 @@ struct pcm_channel { struct { SLIST_ENTRY(pcm_channel) link; } opened; + struct { + SLIST_ENTRY(pcm_channel) link; + } primary; } pcm; } channels; diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c index dcbdd581c82a..88e0580c5c45 100644 --- a/sys/dev/sound/pcm/dsp.c +++ b/sys/dev/sound/pcm/dsp.c @@ -37,6 +37,7 @@ #endif #include <dev/sound/pcm/sound.h> +#include <dev/sound/pcm/vchan.h> #include <sys/ctype.h> #include <sys/lock.h> #include <sys/rwlock.h> @@ -158,6 +159,81 @@ dsp_unlock_chans(struct dsp_cdevpriv *priv, uint32_t prio) CHN_UNLOCK(priv->wrch); } +static int +dsp_chn_alloc(struct snddev_info *d, struct pcm_channel **ch, int direction, + int flags, struct thread *td) +{ + struct pcm_channel *c; + char *comm; + pid_t pid; + int err; + bool vdir_enabled; + + KASSERT(d != NULL && ch != NULL && + (direction == PCMDIR_PLAY || direction == PCMDIR_REC), + ("%s(): invalid d=%p ch=%p direction=%d", + __func__, d, ch, direction)); + PCM_BUSYASSERT(d); + + pid = td->td_proc->p_pid; + comm = td->td_proc->p_comm; + + vdir_enabled = (direction == PCMDIR_PLAY && d->flags & SD_F_PVCHANS) || + (direction == PCMDIR_REC && d->flags & SD_F_RVCHANS); + + *ch = NULL; + CHN_FOREACH(c, d, channels.pcm.primary) { + CHN_LOCK(c); + if (c->direction != direction) { + CHN_UNLOCK(c); + continue; + } + /* Find an available primary channel to use. */ + if ((c->flags & CHN_F_BUSY) == 0 || + (vdir_enabled && (c->flags & CHN_F_HAS_VCHAN))) + break; + CHN_UNLOCK(c); + } + if (c == NULL) + return (EBUSY); + + /* + * We can have the following cases: + * - vchans are enabled, add a new vchan to the primary channel. + * - vchans are disabled, use the primary channel directly. + */ + if (vdir_enabled && ((c->flags & CHN_F_BUSY) == 0 || + c->flags & CHN_F_HAS_VCHAN)) { + err = vchan_create(c, ch); + CHN_UNLOCK(c); + if (err != 0) + return (err); + CHN_LOCK(*ch); + } else if ((c->flags & CHN_F_BUSY) == 0) { + *ch = c; + } else { + CHN_UNLOCK(c); + return (ENODEV); + } + + (*ch)->flags |= CHN_F_BUSY; + if (flags & O_NONBLOCK) + (*ch)->flags |= CHN_F_NBIO; + if (flags & O_EXCL) + (*ch)->flags |= CHN_F_EXCLUSIVE; + (*ch)->pid = pid; + strlcpy((*ch)->comm, (comm != NULL) ? comm : CHN_COMM_UNKNOWN, + sizeof((*ch)->comm)); + + if ((err = chn_reset(*ch, (*ch)->format, (*ch)->speed)) != 0) + return (err); + chn_vpc_reset(*ch, SND_VOL_C_PCM, 0); + + CHN_UNLOCK(*ch); + + return (0); +} + #define DSP_F_VALID(x) ((x) & (FREAD | FWRITE)) #define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE)) #define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x)) @@ -168,7 +244,7 @@ static void dsp_close(void *data) { struct dsp_cdevpriv *priv = data; - struct pcm_channel *rdch, *wrch; + struct pcm_channel *rdch, *wrch, *parent; struct snddev_info *d; int sg_ids; @@ -214,12 +290,20 @@ dsp_close(void *data) if (sg_ids != 0) free_unr(pcmsg_unrhdr, sg_ids); - CHN_LOCK(rdch); - chn_abort(rdch); /* won't sleep */ - rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | - CHN_F_DEAD | CHN_F_EXCLUSIVE); - chn_reset(rdch, 0, 0); - chn_release(rdch); + if (rdch->flags & CHN_F_VIRTUAL) { + parent = rdch->parentchannel; + CHN_LOCK(parent); + CHN_LOCK(rdch); + vchan_destroy(rdch); + CHN_UNLOCK(parent); + } else { + CHN_LOCK(rdch); + chn_abort(rdch); /* won't sleep */ + rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | + CHN_F_DEAD | CHN_F_EXCLUSIVE); + chn_reset(rdch, 0, 0); + chn_release(rdch); + } } if (wrch != NULL) { /* @@ -231,12 +315,20 @@ dsp_close(void *data) if (sg_ids != 0) free_unr(pcmsg_unrhdr, sg_ids); - CHN_LOCK(wrch); - chn_flush(wrch); /* may sleep */ - wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | - CHN_F_DEAD | CHN_F_EXCLUSIVE); - chn_reset(wrch, 0, 0); - chn_release(wrch); + if (wrch->flags & CHN_F_VIRTUAL) { + parent = wrch->parentchannel; + CHN_LOCK(parent); + CHN_LOCK(wrch); + vchan_destroy(wrch); + CHN_UNLOCK(parent); + } else { + CHN_LOCK(wrch); + chn_flush(wrch); /* may sleep */ + wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | + CHN_F_DEAD | CHN_F_EXCLUSIVE); + chn_reset(wrch, 0, 0); + chn_release(wrch); + } } PCM_LOCK(d); } @@ -254,10 +346,9 @@ static int dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) { struct dsp_cdevpriv *priv; - struct pcm_channel *rdch, *wrch, *ch; + struct pcm_channel *ch; struct snddev_info *d; - uint32_t fmt, spd; - int error, rderror, wrerror, dir; + int error, dir; /* Kind of impossible.. */ if (i_dev == NULL || td == NULL) @@ -267,11 +358,11 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) if (!DSP_REGISTERED(d)) return (EBADF); + if (PCM_CHANCOUNT(d) >= PCM_MAXCHANS) + return (ENOMEM); + priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK | M_ZERO); priv->sc = d; - priv->rdch = NULL; - priv->wrch = NULL; - priv->volch = NULL; error = devfs_set_cdevpriv(priv, dsp_close); if (error != 0) @@ -334,98 +425,30 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) PCM_ACQUIRE(d); PCM_UNLOCK(d); - fmt = SND_FORMAT(AFMT_U8, 1, 0); - spd = DSP_DEFAULT_SPEED; - - rdch = NULL; - wrch = NULL; - rderror = 0; - wrerror = 0; - - if (DSP_F_READ(flags)) { - /* open for read */ - rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC, - td->td_proc->p_pid, td->td_proc->p_comm); - - if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0) - rderror = ENXIO; - - if (rderror != 0) { - if (rdch != NULL) - chn_release(rdch); - if (!DSP_F_DUPLEX(flags)) { - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return (rderror); - } - rdch = NULL; - } else { - if (flags & O_NONBLOCK) - rdch->flags |= CHN_F_NBIO; - if (flags & O_EXCL) - rdch->flags |= CHN_F_EXCLUSIVE; - chn_vpc_reset(rdch, SND_VOL_C_PCM, 0); - CHN_UNLOCK(rdch); - } - } - if (DSP_F_WRITE(flags)) { - /* open for write */ - wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, - td->td_proc->p_pid, td->td_proc->p_comm); - - if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0) - wrerror = ENXIO; - - if (wrerror != 0) { - if (wrch != NULL) - chn_release(wrch); - if (!DSP_F_DUPLEX(flags)) { - if (rdch != NULL) { - /* - * Lock, and release previously created - * record channel - */ - CHN_LOCK(rdch); - chn_release(rdch); - } - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return (wrerror); - } - wrch = NULL; - } else { - if (flags & O_NONBLOCK) - wrch->flags |= CHN_F_NBIO; - if (flags & O_EXCL) - wrch->flags |= CHN_F_EXCLUSIVE; - chn_vpc_reset(wrch, SND_VOL_C_PCM, 0); - CHN_UNLOCK(wrch); + error = dsp_chn_alloc(d, &priv->wrch, PCMDIR_PLAY, flags, td); + if (error != 0) { + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (error); } + PCM_LOCK(d); + CHN_INSERT_HEAD(d, priv->wrch, channels.pcm.opened); + PCM_UNLOCK(d); } - - PCM_LOCK(d); - - if (wrch == NULL && rdch == NULL) { - PCM_RELEASE(d); + if (DSP_F_READ(flags)) { + error = dsp_chn_alloc(d, &priv->rdch, PCMDIR_REC, flags, td); + if (error != 0) { + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (error); + } + PCM_LOCK(d); + CHN_INSERT_HEAD(d, priv->rdch, channels.pcm.opened); PCM_UNLOCK(d); - PCM_GIANT_EXIT(d); - if (wrerror != 0) - return (wrerror); - if (rderror != 0) - return (rderror); - return (EINVAL); } - if (rdch != NULL) - CHN_INSERT_HEAD(d, rdch, channels.pcm.opened); - if (wrch != NULL) - CHN_INSERT_HEAD(d, wrch, channels.pcm.opened); - priv->rdch = rdch; - priv->wrch = wrch; - - PCM_RELEASE(d); - PCM_UNLOCK(d); + PCM_RELEASE_QUICK(d); PCM_GIANT_LEAVE(d); return (0); diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c index 2d57852e036d..c0934dde7718 100644 --- a/sys/dev/sound/pcm/sound.c +++ b/sys/dev/sound/pcm/sound.c @@ -107,62 +107,6 @@ snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep); } -int -pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, - pid_t pid, char *comm) -{ - struct pcm_channel *c; - int err, vchancount; - bool retry; - - KASSERT(d != NULL && ch != NULL && - (direction == PCMDIR_PLAY || direction == PCMDIR_REC), - ("%s(): invalid d=%p ch=%p direction=%d pid=%d", - __func__, d, ch, direction, pid)); - PCM_BUSYASSERT(d); - - *ch = NULL; - vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : - d->rvchancount; - retry = false; - -retry_chnalloc: - /* Scan for a free channel. */ - CHN_FOREACH(c, d, channels.pcm) { - CHN_LOCK(c); - if (c->direction != direction) { - CHN_UNLOCK(c); - continue; - } - if (!(c->flags & CHN_F_BUSY)) { - c->flags |= CHN_F_BUSY; - c->pid = pid; - strlcpy(c->comm, (comm != NULL) ? comm : - CHN_COMM_UNKNOWN, sizeof(c->comm)); - *ch = c; - - return (0); - } - CHN_UNLOCK(c); - } - /* Maybe next time... */ - if (retry) - return (EBUSY); - - /* No channel available. We also cannot create more VCHANs. */ - if (!(vchancount > 0 && vchancount < snd_maxautovchans)) - return (ENOTSUP); - - /* Increase the VCHAN count and try to get the new channel. */ - err = vchan_setnew(d, direction, vchancount + 1); - if (err == 0) { - retry = true; - goto retry_chnalloc; - } - - return (err); -} - static int sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) { @@ -472,6 +416,7 @@ pcm_init(device_t dev, void *devinfo) CHN_INIT(d, channels.pcm); CHN_INIT(d, channels.pcm.busy); CHN_INIT(d, channels.pcm.opened); + CHN_INIT(d, channels.pcm.primary); } int @@ -491,7 +436,10 @@ pcm_register(device_t dev, char *str) if (d->playcount > 0 || d->reccount > 0) d->flags |= SD_F_AUTOVCHAN; - vchan_setmaxauto(d, snd_maxautovchans); + if (d->playcount > 0) + d->flags |= SD_F_PVCHANS; + if (d->reccount > 0) + d->flags |= SD_F_RVCHANS; strlcpy(d->status, str, SND_STATUSLEN); sndstat_register(dev, d->status); @@ -733,9 +681,7 @@ sound_global_init(void) if (snd_unit < 0) snd_unit = -1; - if (snd_maxautovchans < 0 || - snd_maxautovchans > SND_MAXVCHANS) - snd_maxautovchans = 0; + snd_vchans_enable = true; if (chn_latency < CHN_LATENCY_MIN || chn_latency > CHN_LATENCY_MAX) @@ -759,11 +705,11 @@ sound_global_init(void) feeder_rate_round = FEEDRATE_ROUNDHZ; if (bootverbose) - printf("%s: snd_unit=%d snd_maxautovchans=%d " + printf("%s: snd_unit=%d snd_vchans_enable=%d " "latency=%d " "feeder_rate_min=%d feeder_rate_max=%d " "feeder_rate_round=%d\n", - __func__, snd_unit, snd_maxautovchans, + __func__, snd_unit, snd_vchans_enable, chn_latency, feeder_rate_min, feeder_rate_max, feeder_rate_round); diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h index 019fe67ac892..0452a58dfcbf 100644 --- a/sys/dev/sound/pcm/sound.h +++ b/sys/dev/sound/pcm/sound.h @@ -99,8 +99,6 @@ struct snd_mixer; #define SOUND_PREFVER SOUND_MODVER #define SOUND_MAXVER SOUND_MODVER -#define SND_MAXVCHANS 256 - #define SD_F_SIMPLEX 0x00000001 #define SD_F_AUTOVCHAN 0x00000002 #define SD_F_SOFTPCMVOL 0x00000004 @@ -113,6 +111,8 @@ struct snd_mixer; #define SD_F_EQ_ENABLED 0x00000200 /* EQ enabled */ #define SD_F_EQ_BYPASSED 0x00000400 /* EQ bypassed */ #define SD_F_EQ_PC 0x00000800 /* EQ per-channel */ +#define SD_F_PVCHANS 0x00001000 /* Playback vchans enabled */ +#define SD_F_RVCHANS 0x00002000 /* Recording vchans enabled */ #define SD_F_EQ_DEFAULT (SD_F_EQ | SD_F_EQ_ENABLED) #define SD_F_EQ_MASK (SD_F_EQ | SD_F_EQ_ENABLED | \ @@ -134,12 +134,15 @@ struct snd_mixer; "\012EQ_ENABLED" \ "\013EQ_BYPASSED" \ "\014EQ_PC" \ + "\015PVCHANS" \ + "\016RVCHANS" \ "\035PRIO_RD" \ "\036PRIO_WR" #define PCM_ALIVE(x) ((x) != NULL && (x)->lock != NULL) #define PCM_REGISTERED(x) (PCM_ALIVE(x) && ((x)->flags & SD_F_REGISTERED)) +#define PCM_MAXCHANS 10000 #define PCM_CHANCOUNT(d) \ (d->playcount + d->pvchancount + d->reccount + d->rvchancount) @@ -168,9 +171,6 @@ extern struct unrhdr *pcmsg_unrhdr; SYSCTL_DECL(_hw_snd); -int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, - pid_t pid, char *comm); - int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo); unsigned int pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz); void pcm_init(device_t dev, void *devinfo); @@ -224,6 +224,9 @@ struct snddev_info { struct { SLIST_HEAD(, pcm_channel) head; } opened; + struct { + SLIST_HEAD(, pcm_channel) head; + } primary; } pcm; } channels; unsigned playcount, reccount, pvchancount, rvchancount; diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c index b0caec3acfec..297120199fe7 100644 --- a/sys/dev/sound/pcm/vchan.c +++ b/sys/dev/sound/pcm/vchan.c @@ -61,7 +61,7 @@ struct vchan_info { int trigger; }; -int snd_maxautovchans = 16; +bool snd_vchans_enable = true; static void * vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, @@ -277,7 +277,7 @@ vchan_getparentchannel(struct snddev_info *d, *ch = NULL; break; } - } else if (c->flags & CHN_F_HAS_VCHAN) { + } else { /* No way!! */ if (*ch != NULL) { CHN_UNLOCK(c); @@ -299,8 +299,7 @@ static int sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; - int direction, vchancount; - int err, cnt; + int err, enabled, flag; d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) @@ -311,43 +310,44 @@ sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS) switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { case VCHAN_PLAY: - direction = PCMDIR_PLAY; - vchancount = d->pvchancount; - cnt = d->playcount; + /* Exit if we do not support this direction. */ + if (d->playcount < 1) { + PCM_UNLOCK(d); + return (ENODEV); + } + flag = SD_F_PVCHANS; break; case VCHAN_REC: - direction = PCMDIR_REC; - vchancount = d->rvchancount; - cnt = d->reccount; + if (d->reccount < 1) { + PCM_UNLOCK(d); + return (ENODEV); + } + flag = SD_F_RVCHANS; break; default: PCM_UNLOCK(d); return (EINVAL); - break; } - if (cnt < 1) { - PCM_UNLOCK(d); - return (ENODEV); - } + enabled = (d->flags & flag) != 0; PCM_ACQUIRE(d); PCM_UNLOCK(d); - cnt = vchancount; - err = sysctl_handle_int(oidp, &cnt, 0, req); - - if (err == 0 && req->newptr != NULL && vchancount != cnt) { - if (cnt < 0) - cnt = 0; - if (cnt > SND_MAXVCHANS) - cnt = SND_MAXVCHANS; - err = vchan_setnew(d, direction, cnt); + err = sysctl_handle_int(oidp, &enabled, 0, req); + if (err != 0 || req->newptr == NULL) { + PCM_RELEASE_QUICK(d); + return (err); } + if (enabled <= 0) + d->flags &= ~flag; + else + d->flags |= flag; + PCM_RELEASE_QUICK(d); - return err; + return (0); } static int @@ -368,15 +368,22 @@ sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS) switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { case VCHAN_PLAY: + if ((d->flags & SD_F_PVCHANS) == 0) { + PCM_UNLOCK(d); + return (ENODEV); + } direction = PCMDIR_PLAY; break; case VCHAN_REC: + if ((d->flags & SD_F_RVCHANS) == 0) { + PCM_UNLOCK(d); + return (ENODEV); + } direction = PCMDIR_REC; break; default: PCM_UNLOCK(d); return (EINVAL); - break; } PCM_ACQUIRE(d); @@ -450,7 +457,7 @@ sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) struct snddev_info *d; struct pcm_channel *c, *ch; struct pcmchan_caps *caps; - int *vchanrate, vchancount, direction, ret, newspd, restart; + int *vchanrate, direction, ret, newspd, restart; d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) @@ -461,24 +468,24 @@ sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { case VCHAN_PLAY: + if ((d->flags & SD_F_PVCHANS) == 0) { + PCM_UNLOCK(d); + return (ENODEV); + } direction = PCMDIR_PLAY; - vchancount = d->pvchancount; vchanrate = &d->pvchanrate; break; case VCHAN_REC: + if ((d->flags & SD_F_RVCHANS) == 0) { + PCM_UNLOCK(d); + return (ENODEV); + } direction = PCMDIR_REC; - vchancount = d->rvchancount; vchanrate = &d->rvchanrate; break; default: PCM_UNLOCK(d); return (EINVAL); - break; - } - - if (vchancount < 1) { - PCM_UNLOCK(d); - return (EINVAL); } PCM_ACQUIRE(d); @@ -555,7 +562,7 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) struct snddev_info *d; struct pcm_channel *c, *ch; uint32_t newfmt; - int *vchanformat, vchancount, direction, ret, restart; + int *vchanformat, direction, ret, restart; char fmtstr[AFMTSTR_LEN]; d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); @@ -567,24 +574,24 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { case VCHAN_PLAY: + if ((d->flags & SD_F_PVCHANS) == 0) { + PCM_UNLOCK(d); + return (ENODEV); + } direction = PCMDIR_PLAY; - vchancount = d->pvchancount; vchanformat = &d->pvchanformat; break; case VCHAN_REC: + if ((d->flags & SD_F_RVCHANS) == 0) { + PCM_UNLOCK(d); + return (ENODEV); + } direction = PCMDIR_REC; - vchancount = d->rvchancount; vchanformat = &d->rvchanformat; break; default: PCM_UNLOCK(d); return (EINVAL); - break; - } - - if (vchancount < 1) { - PCM_UNLOCK(d); - return (EINVAL); } PCM_ACQUIRE(d); @@ -660,7 +667,7 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) "play.vchanrate" : "rec.vchanrate" int -vchan_create(struct pcm_channel *parent) +vchan_create(struct pcm_channel *parent, struct pcm_channel **child) { struct snddev_info *d; struct pcm_channel *ch; @@ -676,9 +683,6 @@ vchan_create(struct pcm_channel *parent) PCM_BUSYASSERT(d); CHN_LOCKASSERT(parent); - if (!(parent->flags & CHN_F_BUSY)) - return (EBUSY); - if (!(parent->direction == PCMDIR_PLAY || parent->direction == PCMDIR_REC)) return (EINVAL); @@ -713,10 +717,12 @@ vchan_create(struct pcm_channel *parent) */ CHN_INSERT_SORT_DESCEND(parent, ch, children); + *child = ch; + if (parent->flags & CHN_F_HAS_VCHAN) return (0); - parent->flags |= CHN_F_HAS_VCHAN; + parent->flags |= CHN_F_HAS_VCHAN | CHN_F_BUSY; parent_caps = chn_getcaps(parent); if (parent_caps == NULL) { @@ -807,6 +813,7 @@ vchan_create(struct pcm_channel *parent) fail: CHN_LOCK(ch); vchan_destroy(ch); + *child = NULL; return (ret); } @@ -878,166 +885,40 @@ vchan_sync(struct pcm_channel *c) return (ret); } -int -vchan_setnew(struct snddev_info *d, int direction, int newcnt) -{ - struct pcm_channel *c, *ch, *nch; - struct pcmchan_caps *caps; - int i, err, vcnt; - - PCM_BUSYASSERT(d); - - if ((direction == PCMDIR_PLAY && d->playcount < 1) || - (direction == PCMDIR_REC && d->reccount < 1)) - return (ENODEV); - - if (!(d->flags & SD_F_AUTOVCHAN)) - return (EINVAL); - - if (newcnt < 0 || newcnt > SND_MAXVCHANS) - return (E2BIG); - - if (direction == PCMDIR_PLAY) - vcnt = d->pvchancount; - else if (direction == PCMDIR_REC) - vcnt = d->rvchancount; - else - return (EINVAL); - - if (newcnt > vcnt) { - /* add new vchans - find a parent channel first */ - ch = NULL; - CHN_FOREACH(c, d, channels.pcm) { - CHN_LOCK(c); - if (c->direction == direction && - ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && - !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { - /* - * Reuse hw channel with vchans already - * created. - */ - if (c->flags & CHN_F_HAS_VCHAN) { - ch = c; - break; - } - /* - * No vchans ever created, look for - * channels with supported formats. - */ - caps = chn_getcaps(c); - if (caps == NULL) { - CHN_UNLOCK(c); - continue; - } - for (i = 0; caps->fmtlist[i] != 0; i++) { - if (caps->fmtlist[i] & AFMT_CONVERTIBLE) - break; - } - if (caps->fmtlist[i] != 0) { - ch = c; - break; - } - } - CHN_UNLOCK(c); - } - if (ch == NULL) - return (EBUSY); - ch->flags |= CHN_F_BUSY; - err = 0; - while (err == 0 && newcnt > vcnt) { - err = vchan_create(ch); - if (err == 0) - vcnt++; - else if (err == E2BIG && newcnt > vcnt) - device_printf(d->dev, - "%s: err=%d Maximum channel reached.\n", - __func__, err); - } - if (vcnt == 0) - ch->flags &= ~CHN_F_BUSY; - CHN_UNLOCK(ch); - if (err != 0) - return (err); - } else if (newcnt < vcnt) { - CHN_FOREACH(c, d, channels.pcm) { - CHN_LOCK(c); - if (c->direction != direction || - CHN_EMPTY(c, children) || - !(c->flags & CHN_F_HAS_VCHAN)) { - CHN_UNLOCK(c); - continue; - } - CHN_FOREACH_SAFE(ch, c, nch, children) { - CHN_LOCK(ch); - if (vcnt == 1 && ch->flags & CHN_F_BUSY) { - CHN_UNLOCK(ch); - break; - } - if (!(ch->flags & CHN_F_BUSY)) { - err = vchan_destroy(ch); - if (err == 0) - vcnt--; - } else - CHN_UNLOCK(ch); - if (vcnt == newcnt) - break; - } - CHN_UNLOCK(c); - break; - } - } - - return (0); *** 122 LINES SKIPPED ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202502251148.51PBmEaZ050898>