Index: share/man/man4/sndstat.4 =================================================================== --- share/man/man4/sndstat.4 +++ share/man/man4/sndstat.4 @@ -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 @@ 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 @@ 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 @@ .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 @@ 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 @@ 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 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 @@ 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 @@ /* 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 @@ * 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); } Index: sys/dev/sound/pcm/sndstat.c =================================================================== --- sys/dev/sound/pcm/sndstat.c +++ sys/dev/sound/pcm/sndstat.c @@ -89,12 +89,13 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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: Index: sys/sys/sndstat.h =================================================================== --- sys/sys/sndstat.h +++ sys/sys/sndstat.h @@ -38,56 +38,65 @@ #include #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