git: ecb0a2ed2f40 - stable/14 - sound: Refactor sndstat_get_caps()

From: Christos Margiolis <christos_at_FreeBSD.org>
Date: Wed, 10 Jul 2024 16:49:11 UTC
The branch stable/14 has been updated by christos:

URL: https://cgit.FreeBSD.org/src/commit/?id=ecb0a2ed2f40ed51b97c17e0be5529b4183dfa32

commit ecb0a2ed2f40ed51b97c17e0be5529b4183dfa32
Author:     Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2024-07-06 18:23:09 +0000
Commit:     Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2024-07-10 16:48:13 +0000

    sound: Refactor sndstat_get_caps()
    
    The current implementation of sndstat_get_caps() does not work properly
    when VCHANs are enabled, as it skips all information about physical
    channels, and also assigns the min/max rates and channels to same
    values, which is usually not the case. A device either supports any
    sample rate within the [feeder_rate_min, feeder_rate_max] range, or
    [hw_rate_min, hw_rate_max] range when the device is opened in exclusive
    or bitperfect mode. The number of channels can also vary and is not
    always the same for both min and max.
    
    Refactor the whole function to resemble the way we handle fetching of
    these values in dsp_oss_audioinfo() and dsp_oss_engineinfo().
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      2 days
    Reviewed by:    dev_submerge.ch
    Differential Revision:  https://reviews.freebsd.org/D45872
    
    (cherry picked from commit 9d8b93bc9ccea82b648ffa9354200c9e4d3f211b)
---
 sys/dev/sound/pcm/sndstat.c | 44 +++++++++++++++++---------------------------
 1 file changed, 17 insertions(+), 27 deletions(-)

diff --git a/sys/dev/sound/pcm/sndstat.c b/sys/dev/sound/pcm/sndstat.c
index 3be376e1da01..5b770810d19b 100644
--- a/sys/dev/sound/pcm/sndstat.c
+++ b/sys/dev/sound/pcm/sndstat.c
@@ -323,47 +323,37 @@ sndstat_write(struct cdev *i_dev, struct uio *buf, int flag)
 }
 
 static void
-sndstat_get_caps(struct snddev_info *d, bool play, uint32_t *min_rate,
+sndstat_get_caps(struct snddev_info *d, int dir, uint32_t *min_rate,
     uint32_t *max_rate, uint32_t *fmts, uint32_t *minchn, uint32_t *maxchn)
 {
 	struct pcm_channel *c;
-	int dir;
-
-	dir = play ? PCMDIR_PLAY : PCMDIR_REC;
-
-	if (play && d->pvchancount > 0) {
-		*min_rate = *max_rate = d->pvchanrate;
-		*fmts = AFMT_ENCODING(d->pvchanformat);
-		*minchn = *maxchn = AFMT_CHANNEL(d->pvchanformat);
-		return;
-	} else if (!play && d->rvchancount > 0) {
-		*min_rate = *max_rate = d->rvchanrate;
-		*fmts = AFMT_ENCODING(d->rvchanformat);
-		*minchn = *maxchn = AFMT_CHANNEL(d->rvchanformat);
-		return;
-	}
+	struct pcmchan_caps *caps;
+	int i;
 
 	*fmts = 0;
 	*min_rate = UINT32_MAX;
 	*max_rate = 0;
 	*minchn = UINT32_MAX;
 	*maxchn = 0;
-	CHN_FOREACH(c, d, channels.pcm) {
-		struct pcmchan_caps *caps;
-		int i;
 
-		if (c->direction != dir || (c->flags & CHN_F_VIRTUAL) != 0)
+	CHN_FOREACH(c, d, channels.pcm) {
+		if (c->direction != dir)
 			continue;
-
 		CHN_LOCK(c);
 		caps = chn_getcaps(c);
-		*min_rate = min(caps->minspeed, *min_rate);
-		*max_rate = max(caps->maxspeed, *max_rate);
 		for (i = 0; caps->fmtlist[i]; i++) {
 			*fmts |= AFMT_ENCODING(caps->fmtlist[i]);
 			*minchn = min(AFMT_CHANNEL(caps->fmtlist[i]), *minchn);
 			*maxchn = max(AFMT_CHANNEL(caps->fmtlist[i]), *maxchn);
 		}
+		if ((c->flags & CHN_F_EXCLUSIVE) ||
+		    (pcm_getflags(d->dev) & SD_F_BITPERFECT)) {
+			*min_rate = min(*min_rate, caps->minspeed);
+			*max_rate = max(*max_rate, caps->maxspeed);
+		} else {
+			*min_rate = min(*min_rate, feeder_rate_min);
+			*max_rate = max(*max_rate, feeder_rate_max);
+		}
 		CHN_UNLOCK(c);
 	}
 	if (*min_rate == UINT32_MAX)
@@ -422,8 +412,8 @@ sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
 	nvlist_add_number(di, SNDST_DSPS_PCHAN, d->playcount);
 	nvlist_add_number(di, SNDST_DSPS_RCHAN, d->reccount);
 	if (d->playcount > 0) {
-		sndstat_get_caps(d, true, &minrate, &maxrate, &fmts, &minchn,
-		    &maxchn);
+		sndstat_get_caps(d, PCMDIR_PLAY, &minrate, &maxrate, &fmts,
+		    &minchn, &maxchn);
 		nvlist_add_number(di, "pminrate", minrate);
 		nvlist_add_number(di, "pmaxrate", maxrate);
 		nvlist_add_number(di, "pfmts", fmts);
@@ -435,8 +425,8 @@ sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
 			nvlist_move_nvlist(di, SNDST_DSPS_INFO_PLAY, diinfo);
 	}
 	if (d->reccount > 0) {
-		sndstat_get_caps(d, false, &minrate, &maxrate, &fmts, &minchn,
-		    &maxchn);
+		sndstat_get_caps(d, PCMDIR_REC, &minrate, &maxrate, &fmts,
+		    &minchn, &maxchn);
 		nvlist_add_number(di, "rminrate", minrate);
 		nvlist_add_number(di, "rmaxrate", maxrate);
 		nvlist_add_number(di, "rfmts", fmts);