sound: Include sound(4) channel information in sndstat nvlist

Extend SNDST_DSPS_PROVIDER_INFO for sound(4) to include information
about each channel in a given device, similar to how cat'ing
/dev/sndstat with hw.snd.verbose=2 works.

While here, document all provider_info fields.

Sponsored by:	The FreeBSD Foundation
MFC after:	3 days
Reviewed by:	dev_submerge.ch, markj
Differential Revision:	https://reviews.freebsd.org/D45501
This commit is contained in:
Christos Margiolis 2024-06-09 17:30:22 +02:00
parent da925fcebf
commit bbca3a75bb
3 changed files with 265 additions and 27 deletions

View file

@ -29,7 +29,7 @@
.\"
.\" Note: The date here should be updated whenever a non-trivial
.\" change is made to the manual page.
.Dd April 15, 2021
.Dd June 5, 2024
.Dt SNDSTAT 4
.Os
.Sh NAME
@ -60,25 +60,55 @@ struct sndstioc_nv_arg {
Here is an example of an nvlist object with explanations of the common fields:
.Bd -literal -offset indent
dsps (NVLIST ARRAY): 1
from_user (BOOL): FALSE
nameunit (STRING): [pcm0]
devnode (STRING): [dsp0]
desc (STRING): [Generic (0x8086) (Analog Line-out)]
pchan (NUMBER): 1 (1) (0x1)
rchan (NUMBER): 0 (0) (0x0)
info_play (NVLIST):
min_rate (NUMBER): 48000 (48000) (0xbb80)
max_rate (NUMBER): 48000 (48000) (0xbb80)
formats (NUMBER): 16 (16) (0x10)
min_chn (NUMBER): 2 (2) (0x2)
max_chn (NUMBER): 2 (2) (0x2)
provider_info (NVLIST):
unit (NUMBER): 0 (0) (0x0)
bitperfect (BOOL): FALSE
pvchan (NUMBER): 1 (1) (0x1)
rvchan (NUMBER): 0 (0) (0x0)
provider (STRING): [sound(4)]
,
from_user (BOOL): FALSE
nameunit (STRING): [pcm0]
devnode (STRING): [dsp0]
desc (STRING): [Generic (0x8086) (Analog Line-out)]
pchan (NUMBER): 1
rchan (NUMBER): 0
info_play (NVLIST):
min_rate (NUMBER): 48000
max_rate (NUMBER): 48000
formats (NUMBER): 16
min_chn (NUMBER): 2
max_chn (NUMBER): 2
provider_info (NVLIST):
unit (NUMBER): 0
bitperfect (BOOL): FALSE
pvchan (NUMBER): 1
rvchan (NUMBER): 0
channel_info (NVLIST_ARRAY): 1
name (STRING): pcm0:virtual_play:dsp0.vp0
parentchan (STRING): pcm0:play:dsp0.p0
unit (NUMBER): 1
latency (NUMBER): 2
rate (NUMBER): 48000
format (NUMBER): 0x201000
pid (NUMBER): 1234
comm (STRING): mpv
interrupts (NUMBER): 0
feedcount (NUMBER): 0
xruns (NUMBER): 0
left_volume (NUMBER): 45
right_volume (NUMBER): 45
hwbuf_fmt (NUMBER): 0x200010
hwbuf_size (NUMBER): 0
hwbuf_blksz (NUMBER): 0
hwbuf_blkcnt (NUMBER): 0
hwbuf_free (NUMBER): 0
hwbuf_ready (NUMBER): 0
swbuf_fmt (NUMBER): 0x201000
swbuf_size (NUMBER): 16384
swbuf_blksz (NUMBER): 2048
swbuf_blkcnt (NUMBER): 8
swbuf_free (NUMBER): 16384
swbuf_ready (NUMBER): 0
feederchain (STRING):
[userland ->
feeder_root(0x00201000) ->
feeder_format(0x00201000 -> 0x00200010) ->
feeder_volume(0x00200010) -> hardware]
provider (STRING): [sound(4)]
.Ed
.Bl -tag -width ".Dv provider_info"
.It Dv from_user
@ -133,6 +163,76 @@ Provider-specific fields.
This field may not exist if the PCM audio device is not provided by in-kernel
interface.
This field will not exist if the provider field is an empty string.
For the
.Xr sound 4
provider, there are a number of name/value pairs inside this field:
.Bl -tag -width ".Dv channel_info"
.It Dv unit
Sound card unit.
.It Dv bitperfect
Whether the sound card has bit-perfect mode enabled.
.It Dv pvchan
Number of playback virtual channels.
.It Dv rvchan
Number of recording virtual channels.
.It Dv channel_info
Channel information.
There are a number of name/value pairs inside this field:
.Bl -tag -width ".Dv hwbuf_blkcnt"
.It Dv name
Channel name.
.It Dv parenchan
Parent channel name (e.g., in the case of virtual channels).
.It Dv unit
Channel unit.
.It Dv latency
Latency.
.It Dv rate
Sampling rate.
.It Dv format
Sampling format.
.It Dv pid
PID of the process consuming the channel.
.It Dv comm
Name of the process consuming the channel.
.It Dv interrupts
Number of interrupts since the channel has been opened.
.It Dv xruns
Number of overruns/underruns, depending on channel direction.
.It Dv feedcount
Number of read/written bytes since the channel has been opened.
.It Dv left_volume
Left volume.
.It Dv right_volume
Right volume.
.It Dv hwbuf_format
Hardware buffer format.
.It Dv hwbuf_size
Hardware buffer size.
.It Dv hwbuf_blksz
Hardware buffer block size.
.It Dv hwbuf_blkcnt
Hardware buffer block count.
.It Dv hwbuf_free
Free space in hardware buffer (in bytes).
.It Dv hwbuf_ready
Number of bytes ready to be read/written from hardware buffer.
.It Dv swbuf_format
Software buffer format.
.It Dv swbuf_size
Software buffer size.
.It Dv swbuf_blksz
Software buffer block size.
.It Dv swbuf_blkcnt
Software buffer block count.
.It Dv swbuf_free
Free space in software buffer (in bytes).
.It Dv swbuf_ready
Number of bytes ready to be read/written from software buffer.
.It Dv feederchain
Channel feeder chain.
.El
.El
.It Dv provider
A string specifying the provider of the PCm audio device.
.El

View file

@ -392,9 +392,12 @@ sndstat_create_diinfo_nv(uint32_t min_rate, uint32_t max_rate, uint32_t formats,
static int
sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
{
struct pcm_channel *c;
struct pcm_feeder *f;
struct sbuf sb;
uint32_t maxrate, minrate, fmts, minchn, maxchn;
nvlist_t *di = NULL, *sound4di = NULL, *diinfo = NULL;
int err;
nvlist_t *di = NULL, *sound4di = NULL, *diinfo = NULL, *cdi = NULL;
int err, nchan;
di = nvlist_create(0);
if (di == NULL) {
@ -451,8 +454,116 @@ sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
sound4di, SNDST_DSPS_SOUND4_BITPERFECT, d->flags & SD_F_BITPERFECT);
nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_PVCHAN, d->pvchancount);
nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_RVCHAN, d->rvchancount);
nchan = 0;
CHN_FOREACH(c, d, channels.pcm) {
sbuf_new(&sb, NULL, 4096, SBUF_AUTOEXTEND);
cdi = nvlist_create(0);
if (cdi == NULL) {
sbuf_delete(&sb);
PCM_RELEASE_QUICK(d);
err = ENOMEM;
goto done;
}
nvlist_add_string(cdi, SNDST_DSPS_SOUND4_CHAN_NAME, c->name);
nvlist_add_string(cdi, SNDST_DSPS_SOUND4_CHAN_PARENTCHAN,
c->parentchannel != NULL ? c->parentchannel->name : "");
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_UNIT, nchan++);
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_LATENCY,
c->latency);
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_RATE, c->speed);
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_FORMAT,
c->format);
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_PID, c->pid);
nvlist_add_string(cdi, SNDST_DSPS_SOUND4_CHAN_COMM, c->comm);
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_INTR,
c->interrupts);
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_FEEDCNT,
c->feedcount);
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_XRUNS, c->xruns);
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_LEFTVOL,
CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_RIGHTVOL,
CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_FORMAT,
sndbuf_getfmt(c->bufhard));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_SIZE,
sndbuf_getsize(c->bufhard));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_BLKSZ,
sndbuf_getblksz(c->bufhard));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_BLKCNT,
sndbuf_getblkcnt(c->bufhard));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_FREE,
sndbuf_getfree(c->bufhard));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_READY,
sndbuf_getready(c->bufhard));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_FORMAT,
sndbuf_getfmt(c->bufsoft));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_SIZE,
sndbuf_getsize(c->bufsoft));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_BLKSZ,
sndbuf_getblksz(c->bufsoft));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_BLKCNT,
sndbuf_getblkcnt(c->bufsoft));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_FREE,
sndbuf_getfree(c->bufsoft));
nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_READY,
sndbuf_getready(c->bufsoft));
sbuf_printf(&sb, "[%s",
(c->direction == PCMDIR_REC) ? "hardware" : "userland");
sbuf_printf(&sb, " -> ");
f = c->feeder;
while (f->source != NULL)
f = f->source;
while (f != NULL) {
sbuf_printf(&sb, "%s", f->class->name);
if (f->desc->type == FEEDER_FORMAT) {
sbuf_printf(&sb, "(0x%08x -> 0x%08x)",
f->desc->in, f->desc->out);
} else if (f->desc->type == FEEDER_MATRIX) {
sbuf_printf(&sb, "(%d.%d -> %d.%d)",
AFMT_CHANNEL(f->desc->in) -
AFMT_EXTCHANNEL(f->desc->in),
AFMT_EXTCHANNEL(f->desc->in),
AFMT_CHANNEL(f->desc->out) -
AFMT_EXTCHANNEL(f->desc->out),
AFMT_EXTCHANNEL(f->desc->out));
} else if (f->desc->type == FEEDER_RATE) {
sbuf_printf(&sb,
"(0x%08x q:%d %d -> %d)",
f->desc->out,
FEEDER_GET(f, FEEDRATE_QUALITY),
FEEDER_GET(f, FEEDRATE_SRC),
FEEDER_GET(f, FEEDRATE_DST));
} else {
sbuf_printf(&sb, "(0x%08x)",
f->desc->out);
}
sbuf_printf(&sb, " -> ");
f = f->parent;
}
sbuf_printf(&sb, "%s]",
(c->direction == PCMDIR_REC) ? "userland" : "hardware");
sbuf_finish(&sb);
nvlist_add_string(cdi, SNDST_DSPS_SOUND4_CHAN_FEEDERCHAIN,
sbuf_data(&sb));
sbuf_delete(&sb);
nvlist_append_nvlist_array(sound4di,
SNDST_DSPS_SOUND4_CHAN_INFO, cdi);
nvlist_destroy(cdi);
err = nvlist_error(sound4di);
if (err) {
PCM_RELEASE_QUICK(d);
goto done;
}
}
nvlist_move_nvlist(di, SNDST_DSPS_PROVIDER_INFO, sound4di);
sound4di = NULL;
PCM_RELEASE_QUICK(d);
nvlist_add_string(di, SNDST_DSPS_PROVIDER, SNDST_DSPS_SOUND4_PROVIDER);

View file

@ -68,11 +68,38 @@ struct sndstioc_nv_arg {
/*
* sound(4)-specific name/value pair names
*/
#define SNDST_DSPS_SOUND4_PROVIDER "sound(4)"
#define SNDST_DSPS_SOUND4_UNIT "unit"
#define SNDST_DSPS_SOUND4_BITPERFECT "bitperfect"
#define SNDST_DSPS_SOUND4_PVCHAN "pvchan"
#define SNDST_DSPS_SOUND4_RVCHAN "rvchan"
#define SNDST_DSPS_SOUND4_PROVIDER "sound(4)"
#define SNDST_DSPS_SOUND4_UNIT "unit"
#define SNDST_DSPS_SOUND4_BITPERFECT "bitperfect"
#define SNDST_DSPS_SOUND4_PVCHAN "pvchan"
#define SNDST_DSPS_SOUND4_RVCHAN "rvchan"
#define SNDST_DSPS_SOUND4_CHAN_INFO "channel_info"
#define SNDST_DSPS_SOUND4_CHAN_NAME "name"
#define SNDST_DSPS_SOUND4_CHAN_PARENTCHAN "parentchan"
#define SNDST_DSPS_SOUND4_CHAN_UNIT "unit"
#define SNDST_DSPS_SOUND4_CHAN_LATENCY "latency"
#define SNDST_DSPS_SOUND4_CHAN_RATE "rate"
#define SNDST_DSPS_SOUND4_CHAN_FORMAT "format"
#define SNDST_DSPS_SOUND4_CHAN_PID "pid"
#define SNDST_DSPS_SOUND4_CHAN_COMM "comm"
#define SNDST_DSPS_SOUND4_CHAN_INTR "interrupts"
#define SNDST_DSPS_SOUND4_CHAN_FEEDCNT "feedcount"
#define SNDST_DSPS_SOUND4_CHAN_XRUNS "xruns"
#define SNDST_DSPS_SOUND4_CHAN_LEFTVOL "left_volume"
#define SNDST_DSPS_SOUND4_CHAN_RIGHTVOL "right_volume"
#define SNDST_DSPS_SOUND4_CHAN_HWBUF_FORMAT "hwbuf_format"
#define SNDST_DSPS_SOUND4_CHAN_HWBUF_SIZE "hwbuf_size"
#define SNDST_DSPS_SOUND4_CHAN_HWBUF_BLKSZ "hwbuf_blksz"
#define SNDST_DSPS_SOUND4_CHAN_HWBUF_BLKCNT "hwbuf_blkcnt"
#define SNDST_DSPS_SOUND4_CHAN_HWBUF_FREE "hwbuf_free"
#define SNDST_DSPS_SOUND4_CHAN_HWBUF_READY "hwbuf_ready"
#define SNDST_DSPS_SOUND4_CHAN_SWBUF_FORMAT "swbuf_format"
#define SNDST_DSPS_SOUND4_CHAN_SWBUF_SIZE "swbuf_size"
#define SNDST_DSPS_SOUND4_CHAN_SWBUF_BLKSZ "swbuf_blksz"
#define SNDST_DSPS_SOUND4_CHAN_SWBUF_BLKCNT "swbuf_blkcnt"
#define SNDST_DSPS_SOUND4_CHAN_SWBUF_FREE "swbuf_free"
#define SNDST_DSPS_SOUND4_CHAN_SWBUF_READY "swbuf_ready"
#define SNDST_DSPS_SOUND4_CHAN_FEEDERCHAIN "feederchain"
/*
* Maximum user-specified nvlist buffer size