sndstat: nvlist schema and API definition changes

- SNDSTAT_LABEL_* are renamed to SNDST_DSPS_*, and SNDSTAT_LABEL_DSPS
  becomes SNDST_DSPS.
- Centralize channel number/rate/formats into a single nvlist
  The above nvlist is named "info_play" and "info_rec"
- Expose only encoding format in pfmts/rfmts. Userland has no direct
  access to AFMT_ENCODING/CHANNEL/EXTCHANNEL macros, thus it serves no
  meaning to expose too much information through this pair of labels.
  However pminrate/rminrate, pmaxrate/rmaxrate, pfmts/rfmts are
  deprecated and will be removed in future.

This commit keeps ioctls ABI compatibility with __FreeBSD_version
1400006 for now. In future the compat ABI with 1400006 will be removed
once audio/virtual_oss is rebuilt.

Sponsored by:	The FreeBSD Foundation
Reviewed by:	hselasky
Approved by:	philip (mentor)
Differential Revision:	https://reviews.freebsd.org/D29770
This commit is contained in:
Ka Ho Ng 2021-04-21 16:19:15 +08:00
parent 438b553207
commit 4ce1ba6523
3 changed files with 322 additions and 181 deletions

View File

@ -31,7 +31,7 @@
.\"
.\" Note: The date here should be updated whenever a non-trivial
.\" change is made to the manual page.
.Dd December 7, 2020
.Dd April 15, 2021
.Dt SNDSTAT 4
.Os
.Sh NAME
@ -52,7 +52,7 @@ device allows callers to enumeration PCM audio devices available for use.
For all ioctls requiring data exchange between the subsystem and callers,
the following structures are used to describe a serialized nvlist:
.Bd -literal -offset indent
struct sndstat_nvlbuf_arg {
struct sndstioc_nv_arg {
size_t nbytes;
void *buf;
};
@ -67,9 +67,12 @@ dsps (NVLIST ARRAY): 1
desc (STRING): [Generic (0x8086) (Analog Line-out)]
pchan (NUMBER): 1 (1) (0x1)
rchan (NUMBER): 0 (0) (0x0)
pminrate (NUMBER): 48000 (48000) (0xbb80)
pmaxrate (NUMBER): 48000 (48000) (0xbb80)
pfmts (NUMBER): 2097168 (2097168) (0x200010)
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
@ -94,24 +97,38 @@ This can be 0 if this PCM audio device does not support playback at all.
.It Dv rchan
The number of recording channels supported by hardware.
This can be 0 if this PCM audio device does not support recording at all.
.It Dv pminrate
The minimum supported playback direction sampling rate.
Only exists if pchan is greater than 0.
.It Dv pmaxrate
The maximum supported playback direction sampling rate.
Only exists if pchan is greater than 0.
.It Dv pfmts
The supported playback direction sample format.
Only exists if pchan is greater than 0.
.It Dv rminrate
The minimum supported recording direction sampling rate.
Only exists if rchan is greater than 0.
.It Dv rmaxrate
The maximum supported recording direction sampling rate.
Only exists if rchan is greater than 0.
.It Dv rfmts
The supported playback recording sample format.
Only exists if rchan is greater than 0.
.It Dv info_play
Supported configurations in playback direction.
This exists only if this PCM audio device supports playback.
There are a number of name/value pairs inside this field:
.Bl -tag -width ".Dv min_rate"
.It Dv min_rate
Minimum supported sampling rate.
.It Dv max_rate
Maximum supported sampling rate.
.It Dv formats
Supported sample formats.
.It Dv min_chn
Minimum supported number of channels in channel layout
.It Dv max_chn
Maximum supported number of channels in channel layout
.El
.It Dv info_rec
Supported configurations in recording direction.
This exists only if this PCM audio device supports recording.
There are a number of name/value pairs inside this field:
.Bl -tag -width ".Dv min_rate"
.It Dv min_rate
Minimum supported sampling rate.
.It Dv max_rate
Maximum supported sampling rate.
.It Dv formats
Supported sample formats.
.It Dv min_chn
Minimum supported number of channels in channel layout
.It Dv max_chn
Maximum supported number of channels in channel layout
.El
.It Dv provider_info
Provider-specific fields.
This field may not exist if the PCM audio device is not provided by in-kernel
@ -121,15 +138,15 @@ This field will not exist if the provider field is an empty string.
A string specifying the provider of the PCm audio device.
.El
.Pp
The following ioctls are providede for use:
.Bl -tag -width ".Dv SNDSTAT_FLUSH_USER_DEVS"
.It Dv SNDSTAT_REFRESH_DEVS
The following ioctls are provided for use:
.Bl -tag -width ".Dv SNDSTIOC_FLUSH_USER_DEVS"
.It Dv SNDSTIOC_REFRESH_DEVS
Drop any previously fetched PCM audio devices list snapshots.
This ioctl takes no arguments.
.It Dv SNDSTAT_GET_DEVS
.It Dv SNDSTIOC_GET_DEVS
Generate and/or return PCM audio devices list snapshots to callers.
This ioctl takes a pointer to
.Fa struct sndstat_nvlbuf_arg
.Fa struct sndstioc_nv_arg
as the first and the only argument.
Callers need to provide a sufficiently large buffer to hold a serialized
nvlist.
@ -159,12 +176,12 @@ Once a PCM audio device list snapshot is returned to user-space successfully,
the snapshot stored in the subsystem's internal structure of the given
.Fa fd
will be freed.
.It Dv SNDSTAT_ADD_USER_DEVS
.It Dv SNDSTIOC_ADD_USER_DEVS
Add a list of PCM audio devices provided by callers to
.Pa /dev/sndstat
device.
This ioctl takes a pointer to
.Fa struct sndstat_nvlbuf_arg
.Fa struct sndstioc_nv_arg
as the first and the only argument.
Callers have to provide a buffer holding a serialized nvlist.
.Fa nbytes
@ -173,7 +190,7 @@ should be set to the length in bytes of the serialized nvlist.
should be pointed to a buffer storing the serialized nvlist.
Userspace-backed PCM audio device nodes should be listed inside the serialized
nvlist.
.It Dv SNDSTAT_FLUSH_USER_DEVS
.It Dv SNDSTIOC_FLUSH_USER_DEVS
Flush any PCM audio devices previously added by callers.
This ioctl takes no arguments.
.El
@ -198,7 +215,7 @@ int
main()
{
int fd;
struct sndstat_nvlbuf_arg arg;
struct sndstioc_nv_arg arg;
const nvlist_t * const *di;
size_t i, nitems;
nvlist_t *nvl;
@ -206,28 +223,28 @@ main()
/* Open sndstat node in read-only first */
fd = open("/dev/sndstat", O_RDONLY);
if (ioctl(fd, SNDSTAT_REFRESH_DEVS, NULL))
err(1, "ioctl(fd, SNDSTAT_REFRESH_DEVS, NULL)");
if (ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL))
err(1, "ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL)");
/* Get the size of snapshot, when nbytes = 0 */
arg.nbytes = 0;
arg.buf = NULL;
if (ioctl(fd, SNDSTAT_GET_DEVS, &arg))
err(1, "ioctl(fd, SNDSTAT_GET_DEVS, &arg)");
if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg))
err(1, "ioctl(fd, SNDSTIOC_GET_DEVS, &arg)");
/* Get snapshot data */
arg.buf = malloc(arg.nbytes);
if (arg.buf == NULL)
err(EX_OSERR, "malloc");
if (ioctl(fd, SNDSTAT_GET_DEVS, &arg))
err(1, "ioctl(fd, SNDSTAT_GET_DEVS, &arg)");
if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg))
err(1, "ioctl(fd, SNDSTIOC_GET_DEVS, &arg)");
/* Deserialize the nvlist stream */
nvl = nvlist_unpack(arg.buf, arg.nbytes, 0);
free(arg.buf);
/* Get DSPs array */
di = nvlist_get_nvlist_array(nvl, SNDSTAT_LABEL_DSPS, &nitems);
di = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems);
for (i = 0; i < nitems; i++) {
const char *nameunit, *devnode, *desc;
@ -235,9 +252,9 @@ main()
* Examine each device nvlist item
*/
nameunit = nvlist_get_string(di[i], SNDSTAT_LABEL_NAMEUNIT);
devnode = nvlist_get_string(di[i], SNDSTAT_LABEL_DEVNODE);
desc = nvlist_get_string(di[i], SNDSTAT_LABEL_DESC);
nameunit = nvlist_get_string(di[i], SNDST_DSPS_NAMEUNIT);
devnode = nvlist_get_string(di[i], SNDST_DSPS_DEVNODE);
desc = nvlist_get_string(di[i], SNDST_DSPS_DESC);
printf("Name unit: `%s`, Device node: `%s`, Description: `%s`\n",
nameunit, devnode, desc);
}

View File

@ -89,12 +89,13 @@ struct sndstat_userdev {
char *desc;
unsigned int pchan;
unsigned int rchan;
uint32_t pminrate;
uint32_t pmaxrate;
uint32_t rminrate;
uint32_t rmaxrate;
uint32_t pfmts;
uint32_t rfmts;
struct {
uint32_t min_rate;
uint32_t max_rate;
uint32_t formats;
uint32_t min_chn;
uint32_t max_chn;
} info_play, info_rec;
nvlist_t *provider_nvl;
};
@ -326,46 +327,77 @@ 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,
uint32_t *max_rate, uint32_t *fmts)
uint32_t *max_rate, uint32_t *fmts, uint32_t *minchn, uint32_t *maxchn)
{
struct pcm_channel *c;
unsigned int encoding;
int dir;
dir = play ? PCMDIR_PLAY : PCMDIR_REC;
*min_rate = 0;
*max_rate = 0;
*fmts = 0;
if (play && d->pvchancount > 0) {
*min_rate = *max_rate = d->pvchanrate;
*fmts = d->pvchanformat;
*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 = d->rvchanformat;
*fmts = AFMT_ENCODING(d->rvchanformat);
*minchn = *maxchn = AFMT_CHANNEL(d->rvchanformat);
return;
}
*min_rate = UINT32_MAX;
*max_rate = 0;
*minchn = UINT32_MAX;
*maxchn = 0;
encoding = 0;
CHN_FOREACH(c, d, channels.pcm) {
struct pcmchan_caps *caps;
int i;
if (c->direction != dir || (c->flags & CHN_F_VIRTUAL) != 0)
continue;
CHN_LOCK(c);
caps = chn_getcaps(c);
*min_rate = caps->minspeed;
*max_rate = caps->maxspeed;
*fmts = chn_getformats(c);
*min_rate = min(caps->minspeed, *min_rate);
*max_rate = max(caps->maxspeed, *max_rate);
for (i = 0; caps->fmtlist[i]; i++) {
encoding |= AFMT_ENCODING(caps->fmtlist[i]);
*minchn = min(AFMT_CHANNEL(encoding), *minchn);
*maxchn = max(AFMT_CHANNEL(encoding), *maxchn);
}
CHN_UNLOCK(c);
}
if (*min_rate == UINT32_MAX)
*min_rate = 0;
if (*minchn == UINT32_MAX)
*minchn = 0;
}
static nvlist_t *
sndstat_create_diinfo_nv(uint32_t min_rate, uint32_t max_rate, uint32_t formats,
uint32_t min_chn, uint32_t max_chn)
{
nvlist_t *nv;
nv = nvlist_create(0);
if (nv == NULL)
return (NULL);
nvlist_add_number(nv, SNDST_DSPS_INFO_MIN_RATE, min_rate);
nvlist_add_number(nv, SNDST_DSPS_INFO_MAX_RATE, max_rate);
nvlist_add_number(nv, SNDST_DSPS_INFO_FORMATS, formats);
nvlist_add_number(nv, SNDST_DSPS_INFO_MIN_CHN, min_chn);
nvlist_add_number(nv, SNDST_DSPS_INFO_MAX_CHN, max_chn);
return (nv);
}
static int
sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
{
uint32_t maxrate, minrate, fmts;
nvlist_t *di = NULL, *sound4di = NULL;
uint32_t maxrate, minrate, fmts, minchn, maxchn;
nvlist_t *di = NULL, *sound4di = NULL, *diinfo = NULL;
int err;
di = nvlist_create(0);
@ -379,40 +411,54 @@ sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
goto done;
}
nvlist_add_bool(di, SNDSTAT_LABEL_FROM_USER, false);
nvlist_add_stringf(di, SNDSTAT_LABEL_NAMEUNIT, "%s",
nvlist_add_bool(di, SNDST_DSPS_FROM_USER, false);
nvlist_add_stringf(di, SNDST_DSPS_NAMEUNIT, "%s",
device_get_nameunit(d->dev));
nvlist_add_stringf(di, SNDSTAT_LABEL_DEVNODE, "dsp%d",
nvlist_add_stringf(di, SNDST_DSPS_DEVNODE, "dsp%d",
device_get_unit(d->dev));
nvlist_add_string(
di, SNDSTAT_LABEL_DESC, device_get_desc(d->dev));
di, SNDST_DSPS_DESC, device_get_desc(d->dev));
PCM_ACQUIRE_QUICK(d);
nvlist_add_number(di, SNDSTAT_LABEL_PCHAN, d->playcount);
nvlist_add_number(di, SNDSTAT_LABEL_RCHAN, d->reccount);
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);
nvlist_add_number(di, SNDSTAT_LABEL_PMINRATE, minrate);
nvlist_add_number(di, SNDSTAT_LABEL_PMAXRATE, maxrate);
nvlist_add_number(di, SNDSTAT_LABEL_PFMTS, fmts);
sndstat_get_caps(d, true, &minrate, &maxrate, &fmts, &minchn,
&maxchn);
nvlist_add_number(di, "pminrate", minrate);
nvlist_add_number(di, "pmaxrate", maxrate);
nvlist_add_number(di, "pfmts", fmts);
diinfo = sndstat_create_diinfo_nv(minrate, maxrate, fmts,
minchn, maxchn);
if (diinfo == NULL)
nvlist_set_error(di, ENOMEM);
else
nvlist_move_nvlist(di, SNDST_DSPS_INFO_PLAY, diinfo);
}
if (d->reccount > 0) {
sndstat_get_caps(d, false, &minrate, &maxrate, &fmts);
nvlist_add_number(di, SNDSTAT_LABEL_RMINRATE, minrate);
nvlist_add_number(di, SNDSTAT_LABEL_RMAXRATE, maxrate);
nvlist_add_number(di, SNDSTAT_LABEL_RFMTS, fmts);
sndstat_get_caps(d, false, &minrate, &maxrate, &fmts, &minchn,
&maxchn);
nvlist_add_number(di, "rminrate", minrate);
nvlist_add_number(di, "rmaxrate", maxrate);
nvlist_add_number(di, "rfmts", fmts);
diinfo = sndstat_create_diinfo_nv(minrate, maxrate, fmts,
minchn, maxchn);
if (diinfo == NULL)
nvlist_set_error(di, ENOMEM);
else
nvlist_move_nvlist(di, SNDST_DSPS_INFO_REC, diinfo);
}
nvlist_add_number(sound4di, SNDSTAT_LABEL_SOUND4_UNIT,
nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_UNIT,
device_get_unit(d->dev)); // XXX: I want signed integer here
nvlist_add_bool(
sound4di, SNDSTAT_LABEL_SOUND4_BITPERFECT, d->flags & SD_F_BITPERFECT);
nvlist_add_number(sound4di, SNDSTAT_LABEL_SOUND4_PVCHAN, d->pvchancount);
nvlist_add_number(sound4di, SNDSTAT_LABEL_SOUND4_RVCHAN, d->rvchancount);
nvlist_move_nvlist(di, SNDSTAT_LABEL_PROVIDER_INFO, sound4di);
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);
nvlist_move_nvlist(di, SNDST_DSPS_PROVIDER_INFO, sound4di);
sound4di = NULL;
PCM_RELEASE_QUICK(d);
nvlist_add_string(di, SNDSTAT_LABEL_PROVIDER, SNDSTAT_LABEL_SOUND4_PROVIDER);
nvlist_add_string(di, SNDST_DSPS_PROVIDER, SNDST_DSPS_SOUND4_PROVIDER);
err = nvlist_error(di);
if (err)
@ -431,7 +477,7 @@ sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
static int
sndstat_build_userland_nvlist(struct sndstat_userdev *ud, nvlist_t **dip)
{
nvlist_t *di;
nvlist_t *di, *diinfo;
int err;
di = nvlist_create(0);
@ -440,34 +486,48 @@ sndstat_build_userland_nvlist(struct sndstat_userdev *ud, nvlist_t **dip)
goto done;
}
nvlist_add_bool(di, SNDSTAT_LABEL_FROM_USER, true);
nvlist_add_number(di, SNDSTAT_LABEL_PCHAN, ud->pchan);
nvlist_add_number(di, SNDSTAT_LABEL_RCHAN, ud->rchan);
nvlist_add_string(di, SNDSTAT_LABEL_NAMEUNIT, ud->nameunit);
nvlist_add_bool(di, SNDST_DSPS_FROM_USER, true);
nvlist_add_number(di, SNDST_DSPS_PCHAN, ud->pchan);
nvlist_add_number(di, SNDST_DSPS_RCHAN, ud->rchan);
nvlist_add_string(di, SNDST_DSPS_NAMEUNIT, ud->nameunit);
nvlist_add_string(
di, SNDSTAT_LABEL_DEVNODE, ud->devnode);
nvlist_add_string(di, SNDSTAT_LABEL_DESC, ud->desc);
di, SNDST_DSPS_DEVNODE, ud->devnode);
nvlist_add_string(di, SNDST_DSPS_DESC, ud->desc);
if (ud->pchan != 0) {
nvlist_add_number(
di, SNDSTAT_LABEL_PMINRATE, ud->pminrate);
nvlist_add_number(
di, SNDSTAT_LABEL_PMAXRATE, ud->pmaxrate);
nvlist_add_number(
di, SNDSTAT_LABEL_PFMTS, ud->pfmts);
nvlist_add_number(di, "pminrate",
ud->info_play.min_rate);
nvlist_add_number(di, "pmaxrate",
ud->info_play.max_rate);
nvlist_add_number(di, "pfmts",
ud->info_play.formats);
diinfo = sndstat_create_diinfo_nv(ud->info_play.min_rate,
ud->info_play.max_rate, ud->info_play.formats,
ud->info_play.min_chn, ud->info_play.max_chn);
if (diinfo == NULL)
nvlist_set_error(di, ENOMEM);
else
nvlist_move_nvlist(di, SNDST_DSPS_INFO_PLAY, diinfo);
}
if (ud->rchan != 0) {
nvlist_add_number(
di, SNDSTAT_LABEL_RMINRATE, ud->rminrate);
nvlist_add_number(
di, SNDSTAT_LABEL_RMAXRATE, ud->rmaxrate);
nvlist_add_number(
di, SNDSTAT_LABEL_RFMTS, ud->rfmts);
nvlist_add_number(di, "rminrate",
ud->info_rec.min_rate);
nvlist_add_number(di, "rmaxrate",
ud->info_rec.max_rate);
nvlist_add_number(di, "rfmts",
ud->info_rec.formats);
diinfo = sndstat_create_diinfo_nv(ud->info_rec.min_rate,
ud->info_rec.max_rate, ud->info_rec.formats,
ud->info_rec.min_chn, ud->info_rec.max_chn);
if (diinfo == NULL)
nvlist_set_error(di, ENOMEM);
else
nvlist_move_nvlist(di, SNDST_DSPS_INFO_REC, diinfo);
}
nvlist_add_string(di, SNDSTAT_LABEL_PROVIDER,
nvlist_add_string(di, SNDST_DSPS_PROVIDER,
(ud->provider != NULL) ? ud->provider : "");
if (ud->provider_nvl != NULL)
nvlist_add_nvlist(
di, SNDSTAT_LABEL_PROVIDER_INFO, ud->provider_nvl);
di, SNDST_DSPS_PROVIDER_INFO, ud->provider_nvl);
err = nvlist_error(di);
if (err)
@ -511,7 +571,7 @@ sndstat_create_devs_nvlist(nvlist_t **nvlp)
if (err)
goto done;
nvlist_append_nvlist_array(nvl, SNDSTAT_LABEL_DSPS, di);
nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
nvlist_destroy(di);
err = nvlist_error(nvl);
if (err)
@ -531,7 +591,7 @@ sndstat_create_devs_nvlist(nvlist_t **nvlp)
sx_xunlock(&pf->lock);
goto done;
}
nvlist_append_nvlist_array(nvl, SNDSTAT_LABEL_DSPS, di);
nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
nvlist_destroy(di);
err = nvlist_error(nvl);
@ -568,7 +628,7 @@ static int
sndstat_get_devs(struct sndstat_file *pf, caddr_t data)
{
int err;
struct sndstat_nvlbuf_arg *arg = (struct sndstat_nvlbuf_arg *)data;
struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data;
SNDSTAT_LOCK();
sx_xlock(&pf->lock);
@ -654,31 +714,65 @@ sndstat_unpack_user_nvlbuf(const void *unvlbuf, size_t nbytes, nvlist_t **nvl)
return (0);
}
static bool
sndstat_diinfo_is_sane(const nvlist_t *diinfo)
{
if (!(nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_RATE) &&
nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_RATE) &&
nvlist_exists_number(diinfo, SNDST_DSPS_INFO_FORMATS) &&
nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MIN_CHN) &&
nvlist_exists_number(diinfo, SNDST_DSPS_INFO_MAX_CHN)))
return (false);
return (true);
}
static bool
sndstat_dsp_nvlist_is_sane(const nvlist_t *nvlist)
{
if (!(nvlist_exists_string(nvlist, SNDSTAT_LABEL_DEVNODE) &&
nvlist_exists_string(nvlist, SNDSTAT_LABEL_DESC) &&
nvlist_exists_number(nvlist, SNDSTAT_LABEL_PCHAN) &&
nvlist_exists_number(nvlist, SNDSTAT_LABEL_RCHAN)))
if (!(nvlist_exists_string(nvlist, SNDST_DSPS_DEVNODE) &&
nvlist_exists_string(nvlist, SNDST_DSPS_DESC) &&
nvlist_exists_number(nvlist, SNDST_DSPS_PCHAN) &&
nvlist_exists_number(nvlist, SNDST_DSPS_RCHAN)))
return (false);
if (nvlist_get_number(nvlist, SNDSTAT_LABEL_PCHAN) > 0)
if (!(nvlist_exists_number(nvlist, SNDSTAT_LABEL_PMINRATE) &&
nvlist_exists_number(nvlist, SNDSTAT_LABEL_PMAXRATE) &&
nvlist_exists_number(nvlist, SNDSTAT_LABEL_PFMTS)))
if (nvlist_get_number(nvlist, SNDST_DSPS_PCHAN) > 0) {
if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) {
if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist,
SNDST_DSPS_INFO_PLAY)))
return (false);
} else if (!(nvlist_exists_number(nvlist, "pminrate") &&
nvlist_exists_number(nvlist, "pmaxrate") &&
nvlist_exists_number(nvlist, "pfmts")))
return (false);
}
if (nvlist_get_number(nvlist, SNDSTAT_LABEL_RCHAN) > 0)
if (!(nvlist_exists_number(nvlist, SNDSTAT_LABEL_RMINRATE) &&
nvlist_exists_number(nvlist, SNDSTAT_LABEL_RMAXRATE) &&
nvlist_exists_number(nvlist, SNDSTAT_LABEL_RFMTS)))
if (nvlist_get_number(nvlist, SNDST_DSPS_RCHAN) > 0) {
if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) {
if (!sndstat_diinfo_is_sane(nvlist_get_nvlist(nvlist,
SNDST_DSPS_INFO_REC)))
return (false);
} else if (!(nvlist_exists_number(nvlist, "rminrate") &&
nvlist_exists_number(nvlist, "rmaxrate") &&
nvlist_exists_number(nvlist, "rfmts")))
return (false);
}
return (true);
}
static void
sndstat_get_diinfo_nv(const nvlist_t *nv, uint32_t *min_rate,
uint32_t *max_rate, uint32_t *formats, uint32_t *min_chn,
uint32_t *max_chn)
{
*min_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_RATE);
*max_rate = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_RATE);
*formats = nvlist_get_number(nv, SNDST_DSPS_INFO_FORMATS);
*min_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MIN_CHN);
*max_chn = nvlist_get_number(nv, SNDST_DSPS_INFO_MAX_CHN);
}
static int
sndstat_dsp_unpack_nvlist(const nvlist_t *nvlist, struct sndstat_userdev *ud)
{
@ -687,36 +781,53 @@ sndstat_dsp_unpack_nvlist(const nvlist_t *nvlist, struct sndstat_userdev *ud)
uint32_t pminrate = 0, pmaxrate = 0;
uint32_t rminrate = 0, rmaxrate = 0;
uint32_t pfmts = 0, rfmts = 0;
uint32_t pminchn = 0, pmaxchn = 0;
uint32_t rminchn = 0, rmaxchn = 0;
nvlist_t *provider_nvl = NULL;
const nvlist_t *diinfo;
const char *provider;
devnode = nvlist_get_string(nvlist, SNDSTAT_LABEL_DEVNODE);
if (nvlist_exists_string(nvlist, SNDSTAT_LABEL_NAMEUNIT))
nameunit = nvlist_get_string(nvlist, SNDSTAT_LABEL_NAMEUNIT);
devnode = nvlist_get_string(nvlist, SNDST_DSPS_DEVNODE);
if (nvlist_exists_string(nvlist, SNDST_DSPS_NAMEUNIT))
nameunit = nvlist_get_string(nvlist, SNDST_DSPS_NAMEUNIT);
else
nameunit = devnode;
desc = nvlist_get_string(nvlist, SNDSTAT_LABEL_DESC);
pchan = nvlist_get_number(nvlist, SNDSTAT_LABEL_PCHAN);
rchan = nvlist_get_number(nvlist, SNDSTAT_LABEL_RCHAN);
desc = nvlist_get_string(nvlist, SNDST_DSPS_DESC);
pchan = nvlist_get_number(nvlist, SNDST_DSPS_PCHAN);
rchan = nvlist_get_number(nvlist, SNDST_DSPS_RCHAN);
if (pchan != 0) {
pminrate = nvlist_get_number(nvlist, SNDSTAT_LABEL_PMINRATE);
pmaxrate = nvlist_get_number(nvlist, SNDSTAT_LABEL_PMAXRATE);
pfmts = nvlist_get_number(nvlist, SNDSTAT_LABEL_PFMTS);
if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_PLAY)) {
diinfo = nvlist_get_nvlist(nvlist,
SNDST_DSPS_INFO_PLAY);
sndstat_get_diinfo_nv(diinfo, &pminrate, &pmaxrate,
&pfmts, &pminchn, &pmaxchn);
} else {
pminrate = nvlist_get_number(nvlist, "pminrate");
pmaxrate = nvlist_get_number(nvlist, "pmaxrate");
pfmts = nvlist_get_number(nvlist, "pfmts");
}
}
if (rchan != 0) {
rminrate = nvlist_get_number(nvlist, SNDSTAT_LABEL_RMINRATE);
rmaxrate = nvlist_get_number(nvlist, SNDSTAT_LABEL_RMAXRATE);
rfmts = nvlist_get_number(nvlist, SNDSTAT_LABEL_RFMTS);
if (nvlist_exists_nvlist(nvlist, SNDST_DSPS_INFO_REC)) {
diinfo = nvlist_get_nvlist(nvlist,
SNDST_DSPS_INFO_REC);
sndstat_get_diinfo_nv(diinfo, &rminrate, &rmaxrate,
&rfmts, &rminchn, &rmaxchn);
} else {
rminrate = nvlist_get_number(nvlist, "rminrate");
rmaxrate = nvlist_get_number(nvlist, "rmaxrate");
rfmts = nvlist_get_number(nvlist, "rfmts");
}
}
provider = dnvlist_get_string(nvlist, SNDSTAT_LABEL_PROVIDER, "");
provider = dnvlist_get_string(nvlist, SNDST_DSPS_PROVIDER, "");
if (provider[0] == '\0')
provider = NULL;
if (provider != NULL &&
nvlist_exists_nvlist(nvlist, SNDSTAT_LABEL_PROVIDER_INFO)) {
nvlist_exists_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO)) {
provider_nvl = nvlist_clone(
nvlist_get_nvlist(nvlist, SNDSTAT_LABEL_PROVIDER_INFO));
nvlist_get_nvlist(nvlist, SNDST_DSPS_PROVIDER_INFO));
if (provider_nvl == NULL)
return (ENOMEM);
}
@ -727,12 +838,16 @@ sndstat_dsp_unpack_nvlist(const nvlist_t *nvlist, struct sndstat_userdev *ud)
ud->desc = strdup(desc, M_DEVBUF);
ud->pchan = pchan;
ud->rchan = rchan;
ud->pminrate = pminrate;
ud->pmaxrate = pmaxrate;
ud->rminrate = rminrate;
ud->rmaxrate = rmaxrate;
ud->pfmts = pfmts;
ud->rfmts = rfmts;
ud->info_play.min_rate = pminrate;
ud->info_play.max_rate = pmaxrate;
ud->info_play.formats = pfmts;
ud->info_play.min_chn = pminchn;
ud->info_play.max_chn = pmaxchn;
ud->info_rec.min_rate = rminrate;
ud->info_rec.max_rate = rmaxrate;
ud->info_rec.formats = rfmts;
ud->info_rec.min_chn = rminchn;
ud->info_rec.max_chn = rmaxchn;
ud->provider_nvl = provider_nvl;
return (0);
}
@ -744,7 +859,7 @@ sndstat_add_user_devs(struct sndstat_file *pf, caddr_t data)
nvlist_t *nvl = NULL;
const nvlist_t * const *dsps;
size_t i, ndsps;
struct sndstat_nvlbuf_arg *arg = (struct sndstat_nvlbuf_arg *)data;
struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data;
if ((pf->fflags & FWRITE) == 0) {
err = EPERM;
@ -755,11 +870,11 @@ sndstat_add_user_devs(struct sndstat_file *pf, caddr_t data)
if (err != 0)
goto done;
if (!nvlist_exists_nvlist_array(nvl, SNDSTAT_LABEL_DSPS)) {
if (!nvlist_exists_nvlist_array(nvl, SNDST_DSPS)) {
err = EINVAL;
goto done;
}
dsps = nvlist_get_nvlist_array(nvl, SNDSTAT_LABEL_DSPS, &ndsps);
dsps = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &ndsps);
for (i = 0; i < ndsps; i++) {
if (!sndstat_dsp_nvlist_is_sane(dsps[i])) {
err = EINVAL;
@ -801,8 +916,8 @@ sndstat_flush_user_devs(struct sndstat_file *pf)
static int
compat_sndstat_get_devs32(struct sndstat_file *pf, caddr_t data)
{
struct sndstat_nvlbuf_arg32 *arg32 = (struct sndstat_nvlbuf_arg32 *)data;
struct sndstat_nvlbuf_arg arg;
struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data;
struct sndstioc_nv_arg arg;
int err;
arg.buf = (void *)(uintptr_t)arg32->buf;
@ -820,8 +935,8 @@ compat_sndstat_get_devs32(struct sndstat_file *pf, caddr_t data)
static int
compat_sndstat_add_user_devs32(struct sndstat_file *pf, caddr_t data)
{
struct sndstat_nvlbuf_arg32 *arg32 = (struct sndstat_nvlbuf_arg32 *)data;
struct sndstat_nvlbuf_arg arg;
struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data;
struct sndstioc_nv_arg arg;
int err;
arg.buf = (void *)(uintptr_t)arg32->buf;
@ -849,11 +964,11 @@ sndstat_ioctl(
return (err);
switch (cmd) {
case SNDSTAT_GET_DEVS:
case SNDSTIOC_GET_DEVS:
err = sndstat_get_devs(pf, data);
break;
#ifdef COMPAT_FREEBSD32
case SNDSTAT_GET_DEVS32:
case SNDSTIOC_GET_DEVS32:
if (!SV_CURPROC_FLAG(SV_ILP32)) {
err = ENODEV;
break;
@ -861,11 +976,11 @@ sndstat_ioctl(
err = compat_sndstat_get_devs32(pf, data);
break;
#endif
case SNDSTAT_ADD_USER_DEVS:
case SNDSTIOC_ADD_USER_DEVS:
err = sndstat_add_user_devs(pf, data);
break;
#ifdef COMPAT_FREEBSD32
case SNDSTAT_ADD_USER_DEVS32:
case SNDSTIOC_ADD_USER_DEVS32:
if (!SV_CURPROC_FLAG(SV_ILP32)) {
err = ENODEV;
break;
@ -873,10 +988,10 @@ sndstat_ioctl(
err = compat_sndstat_add_user_devs32(pf, data);
break;
#endif
case SNDSTAT_REFRESH_DEVS:
case SNDSTIOC_REFRESH_DEVS:
err = sndstat_refresh_devs(pf);
break;
case SNDSTAT_FLUSH_USER_DEVS:
case SNDSTIOC_FLUSH_USER_DEVS:
err = sndstat_flush_user_devs(pf);
break;
default:

View File

@ -38,56 +38,65 @@
#include <sys/ioccom.h>
#endif /* !_IOWR */
struct sndstat_nvlbuf_arg {
struct sndstioc_nv_arg {
size_t nbytes; /* [IN/OUT] buffer size/number of bytes filled */
void *buf; /* [OUT] buffer holding a packed nvlist */
};
/*
* Common labels
* Common name/value pair names
*/
#define SNDSTAT_LABEL_DSPS "dsps"
#define SNDSTAT_LABEL_FROM_USER "from_user"
#define SNDSTAT_LABEL_PCHAN "pchan"
#define SNDSTAT_LABEL_RCHAN "rchan"
#define SNDSTAT_LABEL_PMINRATE "pminrate"
#define SNDSTAT_LABEL_PMAXRATE "pmaxrate"
#define SNDSTAT_LABEL_RMINRATE "rminrate"
#define SNDSTAT_LABEL_RMAXRATE "rmaxrate"
#define SNDSTAT_LABEL_PFMTS "pfmts"
#define SNDSTAT_LABEL_RFMTS "rfmts"
#define SNDSTAT_LABEL_NAMEUNIT "nameunit"
#define SNDSTAT_LABEL_DEVNODE "devnode"
#define SNDSTAT_LABEL_DESC "desc"
#define SNDSTAT_LABEL_PROVIDER "provider"
#define SNDSTAT_LABEL_PROVIDER_INFO "provider_info"
#define SNDST_DSPS "dsps"
#define SNDST_DSPS_FROM_USER "from_user"
#define SNDST_DSPS_PCHAN "pchan"
#define SNDST_DSPS_RCHAN "rchan"
#define SNDST_DSPS_NAMEUNIT "nameunit"
#define SNDST_DSPS_DEVNODE "devnode"
#define SNDST_DSPS_DESC "desc"
#define SNDST_DSPS_PROVIDER "provider"
#define SNDST_DSPS_PROVIDER_INFO "provider_info"
/*
* sound(4)-specific labels
* Common name/value pair names for play/rec info
*/
#define SNDSTAT_LABEL_SOUND4_PROVIDER "sound(4)"
#define SNDSTAT_LABEL_SOUND4_UNIT "unit"
#define SNDSTAT_LABEL_SOUND4_BITPERFECT "bitperfect"
#define SNDSTAT_LABEL_SOUND4_PVCHAN "pvchan"
#define SNDSTAT_LABEL_SOUND4_RVCHAN "rvchan"
#define SNDST_DSPS_INFO_PLAY "info_play"
#define SNDST_DSPS_INFO_REC "info_rec"
#define SNDST_DSPS_INFO_MIN_RATE "min_rate"
#define SNDST_DSPS_INFO_MAX_RATE "max_rate"
#define SNDST_DSPS_INFO_FORMATS "formats"
#define SNDST_DSPS_INFO_MIN_CHN "min_chn"
#define SNDST_DSPS_INFO_MAX_CHN "max_chn"
#define SNDSTAT_REFRESH_DEVS _IO('D', 100)
#define SNDSTAT_GET_DEVS _IOWR('D', 101, struct sndstat_nvlbuf_arg)
#define SNDSTAT_ADD_USER_DEVS _IOWR('D', 102, struct sndstat_nvlbuf_arg)
#define SNDSTAT_FLUSH_USER_DEVS _IO('D', 103)
/*
* 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 SNDSTIOC_REFRESH_DEVS \
_IO('D', 100)
#define SNDSTIOC_GET_DEVS \
_IOWR('D', 101, struct sndstioc_nv_arg)
#define SNDSTIOC_ADD_USER_DEVS \
_IOWR('D', 102, struct sndstioc_nv_arg)
#define SNDSTIOC_FLUSH_USER_DEVS \
_IO('D', 103)
#ifdef _KERNEL
#ifdef COMPAT_FREEBSD32
struct sndstat_nvlbuf_arg32 {
struct sndstioc_nv_arg32 {
uint32_t nbytes;
uint32_t buf;
};
#define SNDSTAT_GET_DEVS32 \
_IOC_NEWTYPE(SNDSTAT_GET_DEVS, struct sndstat_nvlbuf_arg32)
#define SNDSTAT_ADD_USER_DEVS32 \
_IOC_NEWTYPE(SNDSTAT_ADD_USER_DEVS, struct sndstat_nvlbuf_arg32)
#define SNDSTIOC_GET_DEVS32 \
_IOC_NEWTYPE(SNDSTIOC_GET_DEVS, struct sndstioc_nv_arg32)
#define SNDSTIOC_ADD_USER_DEVS32 \
_IOC_NEWTYPE(SNDSTIOC_ADD_USER_DEVS, struct sndstioc_nv_arg32)
#endif
#endif