Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/sndctl/sndctl.c
| Show All 24 Lines | |||||
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
| * SUCH DAMAGE. | * SUCH DAMAGE. | ||||
| */ | */ | ||||
| #include <sys/nv.h> | #include <sys/nv.h> | ||||
| #include <sys/queue.h> | #include <sys/queue.h> | ||||
| #include <sys/sbuf.h> | |||||
| #include <sys/sndstat.h> | #include <sys/sndstat.h> | ||||
| #include <sys/soundcard.h> | #include <sys/soundcard.h> | ||||
| #include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
| #include <err.h> | #include <err.h> | ||||
| #include <errno.h> | #include <errno.h> | ||||
| #include <fcntl.h> | #include <fcntl.h> | ||||
| #include <libgen.h> | #include <libgen.h> | ||||
| #include <limits.h> | #include <limits.h> | ||||
| #include <mixer.h> | #include <mixer.h> | ||||
| #include <stddef.h> | #include <stddef.h> | ||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <unistd.h> | #include <unistd.h> | ||||
| #include <libxo/xo.h> | |||||
| #define SNDCTL_XO_VERSION "1" | |||||
| /* Taken from sys/dev/sound/pcm/ */ | /* Taken from sys/dev/sound/pcm/ */ | ||||
| #define STATUS_LEN 64 | #define STATUS_LEN 64 | ||||
| #define FMTSTR_LEN 16 | #define FMTSTR_LEN 16 | ||||
| struct snd_chan { | struct snd_chan { | ||||
| char name[NAME_MAX]; | char name[NAME_MAX]; | ||||
| char parentchan[NAME_MAX]; | char parentchan[NAME_MAX]; | ||||
| int unit; | int unit; | ||||
| ▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | #define F(member) offsetof(struct snd_dev, member) | ||||
| { "rec.max_chans", F(rec.max_chans), NUM, NULL }, | { "rec.max_chans", F(rec.max_chans), NUM, NULL }, | ||||
| { "rec.formats", F(rec.formats), STR, NULL }, | { "rec.formats", F(rec.formats), STR, NULL }, | ||||
| { NULL, 0, 0, NULL } | { NULL, 0, 0, NULL } | ||||
| #undef F | #undef F | ||||
| }; | }; | ||||
| static struct snd_ctl chan_ctls[] = { | static struct snd_ctl chan_ctls[] = { | ||||
| #define F(member) offsetof(struct snd_chan, member) | #define F(member) offsetof(struct snd_chan, member) | ||||
| /*{ "name", F(name), STR, NULL },*/ | { "name", F(name), STR, NULL }, | ||||
| { "parentchan", F(parentchan), STR, NULL }, | { "parentchan", F(parentchan), STR, NULL }, | ||||
| { "unit", F(unit), NUM, NULL }, | { "unit", F(unit), NUM, NULL }, | ||||
| { "caps", F(caps), STR, NULL }, | { "caps", F(caps), STR, NULL }, | ||||
| { "latency", F(latency), NUM, NULL }, | { "latency", F(latency), NUM, NULL }, | ||||
| { "rate", F(rate), NUM, NULL }, | { "rate", F(rate), NUM, NULL }, | ||||
| { "format", F(format), STR, NULL }, | { "format", F(format), STR, NULL }, | ||||
| { "pid", F(pid), NUM, NULL }, | { "pid", F(pid), NUM, NULL }, | ||||
| { "proc", F(proc), STR, NULL }, | { "proc", F(proc), STR, NULL }, | ||||
| ▲ Show 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | |||||
| sysctl_int(const char *buf, const char *arg, int *var) | sysctl_int(const char *buf, const char *arg, int *var) | ||||
| { | { | ||||
| size_t size; | size_t size; | ||||
| int n, prev; | int n, prev; | ||||
| size = sizeof(int); | size = sizeof(int); | ||||
| /* Read current value. */ | /* Read current value. */ | ||||
| if (sysctlbyname(buf, &prev, &size, NULL, 0) < 0) { | if (sysctlbyname(buf, &prev, &size, NULL, 0) < 0) { | ||||
| warn("sysctlbyname(%s)", buf); | xo_warn("sysctlbyname(%s)", buf); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| /* Read-only. */ | /* Read-only. */ | ||||
| if (arg != NULL) { | if (arg != NULL) { | ||||
| errno = 0; | errno = 0; | ||||
| n = strtol(arg, NULL, 10); | n = strtol(arg, NULL, 10); | ||||
| if (errno == EINVAL || errno == ERANGE) { | if (errno == EINVAL || errno == ERANGE) { | ||||
| warn("strtol(%s)", arg); | xo_warn("strtol(%s)", arg); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| /* Apply new value. */ | /* Apply new value. */ | ||||
| if (sysctlbyname(buf, NULL, 0, &n, size) < 0) { | if (sysctlbyname(buf, NULL, 0, &n, size) < 0) { | ||||
| warn("sysctlbyname(%s, %d)", buf, n); | xo_warn("sysctlbyname(%s, %d)", buf, n); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| } | } | ||||
| /* Read back applied value for good measure. */ | /* Read back applied value for good measure. */ | ||||
| if (sysctlbyname(buf, &n, &size, NULL, 0) < 0) { | if (sysctlbyname(buf, &n, &size, NULL, 0) < 0) { | ||||
| warn("sysctlbyname(%s)", buf); | xo_warn("sysctlbyname(%s)", buf); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| if (arg != NULL) | if (arg != NULL && xo_get_style(NULL) == XO_STYLE_TEXT) | ||||
| printf("%s: %d -> %d\n", buf, prev, n); | printf("%s: %d -> %d\n", buf, prev, n); | ||||
| if (var != NULL) | if (var != NULL) | ||||
| *var = n; | *var = n; | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static int | static int | ||||
| sysctl_str(const char *buf, const char *arg, char *var, size_t varsz) | sysctl_str(const char *buf, const char *arg, char *var, size_t varsz) | ||||
| { | { | ||||
| size_t size; | size_t size; | ||||
| char prev[BUFSIZ]; | char prev[BUFSIZ]; | ||||
| char *tmp; | char *tmp; | ||||
| /* Read current value. */ | /* Read current value. */ | ||||
| size = sizeof(prev); | size = sizeof(prev); | ||||
| if (sysctlbyname(buf, prev, &size, NULL, 0) < 0) { | if (sysctlbyname(buf, prev, &size, NULL, 0) < 0) { | ||||
| warn("sysctlbyname(%s)", buf); | xo_warn("sysctlbyname(%s)", buf); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| /* Read-only. */ | /* Read-only. */ | ||||
| if (arg != NULL) { | if (arg != NULL) { | ||||
| size = strlen(arg); | size = strlen(arg); | ||||
| /* Apply new value. */ | /* Apply new value. */ | ||||
| if (sysctlbyname(buf, NULL, 0, arg, size) < 0) { | if (sysctlbyname(buf, NULL, 0, arg, size) < 0) { | ||||
| warn("sysctlbyname(%s, %s)", buf, arg); | xo_warn("sysctlbyname(%s, %s)", buf, arg); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| /* Get size of new string. */ | /* Get size of new string. */ | ||||
| if (sysctlbyname(buf, NULL, &size, NULL, 0) < 0) { | if (sysctlbyname(buf, NULL, &size, NULL, 0) < 0) { | ||||
| warn("sysctlbyname(%s)", buf); | xo_warn("sysctlbyname(%s)", buf); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| } | } | ||||
| if ((tmp = calloc(1, size)) == NULL) | if ((tmp = calloc(1, size)) == NULL) | ||||
| err(1, "calloc"); | xo_err(1, "calloc"); | ||||
| /* Read back applied value for good measure. */ | /* Read back applied value for good measure. */ | ||||
| if (sysctlbyname(buf, tmp, &size, NULL, 0) < 0) { | if (sysctlbyname(buf, tmp, &size, NULL, 0) < 0) { | ||||
| warn("sysctlbyname(%s)", buf); | xo_warn("sysctlbyname(%s)", buf); | ||||
| free(tmp); | free(tmp); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| if (arg != NULL) | if (arg != NULL && xo_get_style(NULL) == XO_STYLE_TEXT) | ||||
| printf("%s: %s -> %s\n", buf, prev, tmp); | printf("%s: %s -> %s\n", buf, prev, tmp); | ||||
| if (var != NULL) | if (var != NULL) | ||||
| strlcpy(var, tmp, varsz); | strlcpy(var, tmp, varsz); | ||||
| free(tmp); | free(tmp); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static struct snd_dev * | static struct snd_dev * | ||||
| read_dev(char *path) | read_dev(char *path) | ||||
| { | { | ||||
| nvlist_t *nvl; | nvlist_t *nvl; | ||||
| const nvlist_t * const *di; | const nvlist_t * const *di; | ||||
| const nvlist_t * const *cdi; | const nvlist_t * const *cdi; | ||||
| struct sndstioc_nv_arg arg; | struct sndstioc_nv_arg arg; | ||||
| struct snd_dev *dp = NULL; | struct snd_dev *dp = NULL; | ||||
| struct snd_chan *ch; | struct snd_chan *ch; | ||||
| size_t nitems, nchans, i, j; | size_t nitems, nchans, i, j; | ||||
| int fd, caps, unit, t1, t2, t3; | int fd, caps, unit, t1, t2, t3; | ||||
| if ((fd = open("/dev/sndstat", O_RDONLY)) < 0) | if ((fd = open("/dev/sndstat", O_RDONLY)) < 0) | ||||
| err(1, "open(/dev/sndstat)"); | xo_err(1, "open(/dev/sndstat)"); | ||||
| if (ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL) < 0) | if (ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL) < 0) | ||||
| err(1, "ioctl(SNDSTIOC_REFRESH_DEVS)"); | xo_err(1, "ioctl(SNDSTIOC_REFRESH_DEVS)"); | ||||
| arg.nbytes = 0; | arg.nbytes = 0; | ||||
| arg.buf = NULL; | arg.buf = NULL; | ||||
| if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0) | if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0) | ||||
| err(1, "ioctl(SNDSTIOC_GET_DEVS#1)"); | xo_err(1, "ioctl(SNDSTIOC_GET_DEVS#1)"); | ||||
| if ((arg.buf = malloc(arg.nbytes)) == NULL) | if ((arg.buf = malloc(arg.nbytes)) == NULL) | ||||
| err(1, "malloc"); | xo_err(1, "malloc"); | ||||
| if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0) | if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0) | ||||
| err(1, "ioctl(SNDSTIOC_GET_DEVS#2)"); | xo_err(1, "ioctl(SNDSTIOC_GET_DEVS#2)"); | ||||
| if ((nvl = nvlist_unpack(arg.buf, arg.nbytes, 0)) == NULL) | if ((nvl = nvlist_unpack(arg.buf, arg.nbytes, 0)) == NULL) | ||||
| err(1, "nvlist_unpack"); | xo_err(1, "nvlist_unpack"); | ||||
| if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS)) | if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS)) | ||||
| errx(1, "no soundcards attached"); | xo_errx(1, "no soundcards attached"); | ||||
| if (path == NULL || (path != NULL && strcmp(basename(path), "dsp") == 0)) | if (path == NULL || (path != NULL && strcmp(basename(path), "dsp") == 0)) | ||||
| unit = mixer_get_dunit(); | unit = mixer_get_dunit(); | ||||
| else | else | ||||
| unit = -1; | unit = -1; | ||||
| /* Find whether the requested device exists */ | /* Find whether the requested device exists */ | ||||
| di = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems); | di = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems); | ||||
| for (i = 0; i < nitems; i++) { | for (i = 0; i < nitems; i++) { | ||||
| if (unit == -1 && strcmp(basename(path), | if (unit == -1 && strcmp(basename(path), | ||||
| nvlist_get_string(di[i], SNDST_DSPS_DEVNODE)) == 0) | nvlist_get_string(di[i], SNDST_DSPS_DEVNODE)) == 0) | ||||
| break; | break; | ||||
| else if (nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO) && | else if (nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO) && | ||||
| (int)nvlist_get_number(nvlist_get_nvlist(di[i], | (int)nvlist_get_number(nvlist_get_nvlist(di[i], | ||||
| SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_UNIT) == unit) | SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_UNIT) == unit) | ||||
| break;; | break;; | ||||
| } | } | ||||
| if (i == nitems) | if (i == nitems) | ||||
| errx(1, "device not found"); | xo_errx(1, "device not found"); | ||||
| #define NV(type, item) \ | #define NV(type, item) \ | ||||
| nvlist_get_ ## type (di[i], SNDST_DSPS_ ## item) | nvlist_get_ ## type (di[i], SNDST_DSPS_ ## item) | ||||
| if ((dp = calloc(1, sizeof(struct snd_dev))) == NULL) | if ((dp = calloc(1, sizeof(struct snd_dev))) == NULL) | ||||
| err(1, "calloc"); | xo_err(1, "calloc"); | ||||
| dp->unit = -1; | dp->unit = -1; | ||||
| strlcpy(dp->name, NV(string, NAMEUNIT), sizeof(dp->name)); | strlcpy(dp->name, NV(string, NAMEUNIT), sizeof(dp->name)); | ||||
| strlcpy(dp->desc, NV(string, DESC), sizeof(dp->desc)); | strlcpy(dp->desc, NV(string, DESC), sizeof(dp->desc)); | ||||
| strlcpy(dp->devnode, NV(string, DEVNODE), sizeof(dp->devnode)); | strlcpy(dp->devnode, NV(string, DEVNODE), sizeof(dp->devnode)); | ||||
| dp->from_user = NV(bool, FROM_USER); | dp->from_user = NV(bool, FROM_USER); | ||||
| dp->play.pchans = NV(number, PCHAN); | dp->play.pchans = NV(number, PCHAN); | ||||
| dp->rec.pchans = NV(number, RCHAN); | dp->rec.pchans = NV(number, RCHAN); | ||||
| #undef NV | #undef NV | ||||
| if (dp->play.pchans && !nvlist_exists(di[i], SNDST_DSPS_INFO_PLAY)) | if (dp->play.pchans && !nvlist_exists(di[i], SNDST_DSPS_INFO_PLAY)) | ||||
| errx(1, "%s: playback channel list empty", dp->name); | xo_errx(1, "%s: playback channel list empty", dp->name); | ||||
| if (dp->rec.pchans && !nvlist_exists(di[i], SNDST_DSPS_INFO_REC)) | if (dp->rec.pchans && !nvlist_exists(di[i], SNDST_DSPS_INFO_REC)) | ||||
| errx(1, "%s: recording channel list empty", dp->name); | xo_errx(1, "%s: recording channel list empty", dp->name); | ||||
| #define NV(type, mode, item) \ | #define NV(type, mode, item) \ | ||||
| nvlist_get_ ## type (nvlist_get_nvlist(di[i], \ | nvlist_get_ ## type (nvlist_get_nvlist(di[i], \ | ||||
| SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item) | SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item) | ||||
| if (dp->play.pchans) { | if (dp->play.pchans) { | ||||
| dp->play.min_rate = NV(number, PLAY, MIN_RATE); | dp->play.min_rate = NV(number, PLAY, MIN_RATE); | ||||
| dp->play.max_rate = NV(number, PLAY, MAX_RATE); | dp->play.max_rate = NV(number, PLAY, MAX_RATE); | ||||
| dp->play.min_chans = NV(number, PLAY, MIN_CHN); | dp->play.min_chans = NV(number, PLAY, MIN_CHN); | ||||
| Show All 15 Lines | #undef NV | ||||
| * Skip further parsing if the provider is not sound(4), as the | * Skip further parsing if the provider is not sound(4), as the | ||||
| * following code is sound(4)-specific. | * following code is sound(4)-specific. | ||||
| */ | */ | ||||
| if (strcmp(nvlist_get_string(di[i], SNDST_DSPS_PROVIDER), | if (strcmp(nvlist_get_string(di[i], SNDST_DSPS_PROVIDER), | ||||
| SNDST_DSPS_SOUND4_PROVIDER) != 0) | SNDST_DSPS_SOUND4_PROVIDER) != 0) | ||||
| goto done; | goto done; | ||||
| if (!nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO)) | if (!nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO)) | ||||
| errx(1, "%s: provider_info list empty", dp->name); | xo_errx(1, "%s: provider_info list empty", dp->name); | ||||
| #define NV(type, item) \ | #define NV(type, item) \ | ||||
| nvlist_get_ ## type (nvlist_get_nvlist(di[i], \ | nvlist_get_ ## type (nvlist_get_nvlist(di[i], \ | ||||
| SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item) | SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item) | ||||
| strlcpy(dp->status, NV(string, STATUS), sizeof(dp->status)); | strlcpy(dp->status, NV(string, STATUS), sizeof(dp->status)); | ||||
| dp->unit = NV(number, UNIT); | dp->unit = NV(number, UNIT); | ||||
| dp->bitperfect = NV(bool, BITPERFECT); | dp->bitperfect = NV(bool, BITPERFECT); | ||||
| dp->play.vchans = NV(bool, PVCHAN); | dp->play.vchans = NV(bool, PVCHAN); | ||||
| dp->play.rate = NV(number, PVCHANRATE); | dp->play.rate = NV(number, PVCHANRATE); | ||||
| fmt2str(dp->play.format, sizeof(dp->play.format), | fmt2str(dp->play.format, sizeof(dp->play.format), | ||||
| NV(number, PVCHANFORMAT)); | NV(number, PVCHANFORMAT)); | ||||
| dp->rec.vchans = NV(bool, RVCHAN); | dp->rec.vchans = NV(bool, RVCHAN); | ||||
| dp->rec.rate = NV(number, RVCHANRATE); | dp->rec.rate = NV(number, RVCHANRATE); | ||||
| fmt2str(dp->rec.format, sizeof(dp->rec.format), | fmt2str(dp->rec.format, sizeof(dp->rec.format), | ||||
| NV(number, RVCHANFORMAT)); | NV(number, RVCHANFORMAT)); | ||||
| #undef NV | #undef NV | ||||
| dp->autoconv = (dp->play.vchans || dp->rec.vchans) && !dp->bitperfect; | dp->autoconv = (dp->play.vchans || dp->rec.vchans) && !dp->bitperfect; | ||||
| if (sysctl_int("hw.snd.latency", NULL, &t1) || | if (sysctl_int("hw.snd.latency", NULL, &t1) || | ||||
| sysctl_int("hw.snd.latency_profile", NULL, &t2) || | sysctl_int("hw.snd.latency_profile", NULL, &t2) || | ||||
| sysctl_int("kern.timecounter.alloweddeviation", NULL, &t3)) | sysctl_int("kern.timecounter.alloweddeviation", NULL, &t3)) | ||||
| err(1, "%s: sysctl", dp->name); | xo_err(1, "%s: sysctl", dp->name); | ||||
| if (t1 == 0 && t2 == 0 && t3 == 0) | if (t1 == 0 && t2 == 0 && t3 == 0) | ||||
| dp->realtime = 1; | dp->realtime = 1; | ||||
| if (!nvlist_exists(nvlist_get_nvlist(di[i], | if (!nvlist_exists(nvlist_get_nvlist(di[i], | ||||
| SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_CHAN_INFO)) | SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_CHAN_INFO)) | ||||
| errx(1, "%s: channel info list empty", dp->name); | xo_errx(1, "%s: channel info list empty", dp->name); | ||||
| cdi = nvlist_get_nvlist_array( | cdi = nvlist_get_nvlist_array( | ||||
| nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO), | nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO), | ||||
| SNDST_DSPS_SOUND4_CHAN_INFO, &nchans); | SNDST_DSPS_SOUND4_CHAN_INFO, &nchans); | ||||
| TAILQ_INIT(&dp->chans); | TAILQ_INIT(&dp->chans); | ||||
| caps = 0; | caps = 0; | ||||
| for (j = 0; j < nchans; j++) { | for (j = 0; j < nchans; j++) { | ||||
| #define NV(type, item) \ | #define NV(type, item) \ | ||||
| nvlist_get_ ## type (cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item) | nvlist_get_ ## type (cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item) | ||||
| if ((ch = calloc(1, sizeof(struct snd_chan))) == NULL) | if ((ch = calloc(1, sizeof(struct snd_chan))) == NULL) | ||||
| err(1, "calloc"); | xo_err(1, "calloc"); | ||||
| strlcpy(ch->name, NV(string, NAME), sizeof(ch->name)); | strlcpy(ch->name, NV(string, NAME), sizeof(ch->name)); | ||||
| strlcpy(ch->parentchan, NV(string, PARENTCHAN), | strlcpy(ch->parentchan, NV(string, PARENTCHAN), | ||||
| sizeof(ch->parentchan)); | sizeof(ch->parentchan)); | ||||
| ch->unit = NV(number, UNIT); | ch->unit = NV(number, UNIT); | ||||
| ch->direction = (NV(number, CAPS) & PCM_CAP_INPUT) ? | ch->direction = (NV(number, CAPS) & PCM_CAP_INPUT) ? | ||||
| INPUT : OUTPUT; | INPUT : OUTPUT; | ||||
| cap2str(ch->caps, sizeof(ch->caps), NV(number, CAPS)); | cap2str(ch->caps, sizeof(ch->caps), NV(number, CAPS)); | ||||
| ▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | |||||
| static void | static void | ||||
| print_dev_ctl(struct snd_dev *dp, struct snd_ctl *ctl, bool simple, | print_dev_ctl(struct snd_dev *dp, struct snd_ctl *ctl, bool simple, | ||||
| bool showgrp) | bool showgrp) | ||||
| { | { | ||||
| struct snd_ctl *cp; | struct snd_ctl *cp; | ||||
| size_t len; | size_t len; | ||||
| if (ctl->type != GRP) { | if (ctl->type != GRP && xo_get_style(NULL) == XO_STYLE_TEXT) { | ||||
| if (simple) | if (simple) | ||||
| printf("%s=", ctl->name); | printf("%s=", ctl->name); | ||||
| else | else | ||||
| printf(" %-20s= ", ctl->name); | printf(" %-20s= ", ctl->name); | ||||
| } | } | ||||
| switch (ctl->type) { | switch (ctl->type) { | ||||
| case STR: | case STR: | ||||
| printf("%s\n", (char *)dp + ctl->off); | xo_emit("{a:%s/%s}\n", ctl->name, (char *)dp + ctl->off); | ||||
| break; | break; | ||||
| case NUM: | case NUM: | ||||
| printf("%d\n", *(int *)((intptr_t)dp + ctl->off)); | xo_emit("{a:%s/%d}\n", ctl->name, *(int *)((intptr_t)dp + ctl->off)); | ||||
| break; | break; | ||||
| case VOL: | case VOL: | ||||
| break; | break; | ||||
| case GRP: | case GRP: | ||||
| if (!simple || !showgrp) | if (!simple || !showgrp) | ||||
| break; | break; | ||||
| for (cp = dev_ctls; cp->name != NULL; cp++) { | for (cp = dev_ctls; cp->name != NULL; cp++) { | ||||
| len = strlen(ctl->name); | len = strlen(ctl->name); | ||||
| if (strncmp(ctl->name, cp->name, len) == 0 && | if (strncmp(ctl->name, cp->name, len) == 0 && | ||||
| cp->name[len] == '.' && cp->type != GRP) | cp->name[len] == '.' && cp->type != GRP) | ||||
| print_dev_ctl(dp, cp, simple, showgrp); | print_dev_ctl(dp, cp, simple, showgrp); | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| static void | static void | ||||
| print_chan_ctl(struct snd_chan *ch, struct snd_ctl *ctl, bool simple, | print_chan_ctl(struct snd_chan *ch, struct snd_ctl *ctl, bool simple, | ||||
| bool showgrp) | bool showgrp) | ||||
| { | { | ||||
| struct snd_ctl *cp; | struct snd_ctl *cp; | ||||
| size_t len; | size_t len; | ||||
| int v; | int v; | ||||
| if (ctl->type != GRP) { | if (ctl->type != GRP && xo_get_style(NULL) == XO_STYLE_TEXT) { | ||||
| if (simple) | if (simple) | ||||
| printf("%s.%s=", ch->name, ctl->name); | printf("%s.%s=", ch->name, ctl->name); | ||||
| else | else | ||||
| printf(" %-20s= ", ctl->name); | printf(" %-20s= ", ctl->name); | ||||
| } | } | ||||
| switch (ctl->type) { | switch (ctl->type) { | ||||
| case STR: | case STR: | ||||
| printf("%s\n", (char *)ch + ctl->off); | xo_emit("{a:%s/%s}\n", ctl->name, (char *)ch + ctl->off); | ||||
| break; | break; | ||||
| case NUM: | case NUM: | ||||
| printf("%d\n", *(int *)((intptr_t)ch + ctl->off)); | xo_emit("{a:%s/%d}\n", ctl->name, *(int *)((intptr_t)ch + ctl->off)); | ||||
| break; | break; | ||||
| case VOL: | case VOL: | ||||
| v = *(int *)((intptr_t)ch + ctl->off); | v = *(int *)((intptr_t)ch + ctl->off); | ||||
| printf("%.2f:%.2f\n", | xo_emit("{a:%s/%.2f:%.2f}\n", ctl->name, | ||||
| MIX_VOLNORM(v & 0x00ff), MIX_VOLNORM((v >> 8) & 0x00ff)); | MIX_VOLNORM(v & 0x00ff), MIX_VOLNORM((v >> 8) & 0x00ff)); | ||||
| break; | break; | ||||
| case GRP: | case GRP: | ||||
| if (!simple || !showgrp) | if (!simple || !showgrp) | ||||
| break; | break; | ||||
| for (cp = chan_ctls; cp->name != NULL; cp++) { | for (cp = chan_ctls; cp->name != NULL; cp++) { | ||||
| len = strlen(ctl->name); | len = strlen(ctl->name); | ||||
| if (strncmp(ctl->name, cp->name, len) == 0 && | if (strncmp(ctl->name, cp->name, len) == 0 && | ||||
| cp->name[len] == '.' && cp->type != GRP) | cp->name[len] == '.' && cp->type != GRP) | ||||
| print_chan_ctl(ch, cp, simple, showgrp); | print_chan_ctl(ch, cp, simple, showgrp); | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| static void | static void | ||||
| print_dev(struct snd_dev *dp) | print_dev(struct snd_dev *dp) | ||||
| { | { | ||||
| struct snd_chan *ch; | struct snd_chan *ch; | ||||
| struct snd_ctl *ctl; | struct snd_ctl *ctl; | ||||
| struct sbuf sb; | |||||
| char buf[16]; | |||||
| if (!oflag) { | xo_open_instance("devices"); | ||||
| printf("%s: <%s> %s", dp->name, dp->desc, dp->status); | |||||
| printf(" ("); | if (!oflag || xo_get_style(NULL) != XO_STYLE_TEXT) { | ||||
| sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN); | |||||
| sbuf_printf(&sb, "("); | |||||
| if (dp->play.pchans) | if (dp->play.pchans) | ||||
| printf("play"); | sbuf_printf(&sb, "play"); | ||||
| if (dp->play.pchans && dp->rec.pchans) | if (dp->play.pchans && dp->rec.pchans) | ||||
| printf("/"); | sbuf_printf(&sb, "/"); | ||||
| if (dp->rec.pchans) | if (dp->rec.pchans) | ||||
| printf("rec"); | sbuf_printf(&sb, "rec"); | ||||
| printf(")\n"); | sbuf_printf(&sb, ")"); | ||||
| xo_emit("{:header/%s: <%s> %s %s}\n", | |||||
| dp->name, dp->desc, dp->status, sbuf_data(&sb)); | |||||
| sbuf_delete(&sb); | |||||
| } | } | ||||
| for (ctl = dev_ctls; ctl->name != NULL; ctl++) | for (ctl = dev_ctls; ctl->name != NULL; ctl++) | ||||
| print_dev_ctl(dp, ctl, oflag, false); | print_dev_ctl(dp, ctl, oflag, false); | ||||
| if (vflag) { | if (vflag) { | ||||
| xo_open_list("channels"); | |||||
| TAILQ_FOREACH(ch, &dp->chans, next) { | TAILQ_FOREACH(ch, &dp->chans, next) { | ||||
| if (!oflag) | xo_open_instance("channels"); | ||||
| if (!oflag && xo_get_style(NULL) == XO_STYLE_TEXT) | |||||
| printf(" %s\n", ch->name); | printf(" %s\n", ch->name); | ||||
| for (ctl = chan_ctls; ctl->name != NULL; ctl++) | for (ctl = chan_ctls; ctl->name != NULL; ctl++) | ||||
| print_chan_ctl(ch, ctl, oflag, false); | print_chan_ctl(ch, ctl, oflag, false); | ||||
| xo_close_instance("channels"); | |||||
| } | } | ||||
| xo_close_list("channels"); | |||||
| } | } | ||||
| xo_close_instance("devices"); | |||||
| } | } | ||||
| static int | static int | ||||
| mod_bitperfect(struct snd_dev *dp, void *arg) | mod_bitperfect(struct snd_dev *dp, void *arg) | ||||
| { | { | ||||
| char buf[64]; | char buf[64]; | ||||
| if (dp->from_user) | if (dp->from_user) | ||||
| ▲ Show 20 Lines • Show All 147 Lines • ▼ Show 20 Lines | mod_rec_format(struct snd_dev *dp, void *arg) | ||||
| snprintf(buf, sizeof(buf), "dev.pcm.%d.rec.vchanformat", dp->unit); | snprintf(buf, sizeof(buf), "dev.pcm.%d.rec.vchanformat", dp->unit); | ||||
| return (sysctl_str(buf, arg, dp->rec.format, sizeof(dp->rec.format))); | return (sysctl_str(buf, arg, dp->rec.format, sizeof(dp->rec.format))); | ||||
| } | } | ||||
| static void __dead2 | static void __dead2 | ||||
| usage(void) | usage(void) | ||||
| { | { | ||||
| fprintf(stderr, "usage: %s [-f device] [-hov] [control[=value] ...]\n", | xo_error("usage: %s [--libxo] [-f device] [-hov] [control[=value] ...]\n", | ||||
| getprogname()); | getprogname()); | ||||
| xo_finish(); | |||||
| exit(1); | exit(1); | ||||
| } | } | ||||
| int | int | ||||
| main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||||
| { | { | ||||
| struct snd_dev *dp; | struct snd_dev *dp; | ||||
| struct snd_chan *ch; | struct snd_chan *ch; | ||||
| struct snd_ctl *ctl; | struct snd_ctl *ctl; | ||||
| char *path = NULL; | char *path = NULL; | ||||
| char *s, *propstr; | char *s, *propstr; | ||||
| bool show = true, found; | bool show = true, found; | ||||
| int c; | int c; | ||||
| argc = xo_parse_args(argc, argv); | |||||
| if (argc < 0) | |||||
| exit(1); | |||||
| while ((c = getopt(argc, argv, "f:hov")) != -1) { | while ((c = getopt(argc, argv, "f:hov")) != -1) { | ||||
| switch (c) { | switch (c) { | ||||
| case 'f': | case 'f': | ||||
| path = optarg; | path = optarg; | ||||
| break; | break; | ||||
| case 'o': | case 'o': | ||||
| oflag = true; | oflag = true; | ||||
| break; | break; | ||||
| case 'v': | case 'v': | ||||
| vflag = true; | vflag = true; | ||||
| break; | break; | ||||
| case 'h': /* FALLTHROUGH */ | case 'h': /* FALLTHROUGH */ | ||||
| case '?': | case '?': | ||||
| default: | default: | ||||
| usage(); | usage(); | ||||
| } | } | ||||
| } | } | ||||
| argc -= optind; | argc -= optind; | ||||
| argv += optind; | argv += optind; | ||||
| xo_set_version(SNDCTL_XO_VERSION); | |||||
| xo_open_container("sndctl"); | |||||
| dp = read_dev(path); | dp = read_dev(path); | ||||
| xo_open_container("executed_controls"); | |||||
| while (argc > 0) { | while (argc > 0) { | ||||
| if ((s = strdup(*argv)) == NULL) | if ((s = strdup(*argv)) == NULL) { | ||||
| err(1, "strdup(%s)", *argv); | xo_close_container("executed_controls"); | ||||
| xo_close_container("sndctl"); | |||||
| if (xo_finish() < 0) | |||||
| xo_err(1, "xo_finish"); | |||||
| xo_err(1, "strdup(%s)", *argv); | |||||
| } | |||||
| propstr = strsep(&s, "="); | propstr = strsep(&s, "="); | ||||
| if (propstr == NULL) | if (propstr == NULL) | ||||
| goto next; | goto next; | ||||
| found = false; | found = false; | ||||
| for (ctl = dev_ctls; ctl->name != NULL; ctl++) { | for (ctl = dev_ctls; ctl->name != NULL; ctl++) { | ||||
| if (strcmp(ctl->name, propstr) != 0) | if (strcmp(ctl->name, propstr) != 0) | ||||
| continue; | continue; | ||||
| if (s == NULL) { | if (s == NULL) | ||||
| print_dev_ctl(dp, ctl, true, true); | |||||
| show = false; | show = false; | ||||
| } else if (ctl->mod != NULL && ctl->mod(dp, s) < 0) | else if (ctl->mod != NULL && ctl->mod(dp, s) < 0) | ||||
| warnx("%s(%s) failed", ctl->name, s); | xo_warnx("%s(%s) failed", ctl->name, s); | ||||
| if (s == NULL || xo_get_style(NULL) != XO_STYLE_TEXT) { | |||||
| /* | |||||
| * Print the control in libxo mode in all | |||||
| * cases, otherwise we'll not be printing any | |||||
| * controls that were modified or whose | |||||
| * ctl->mod() failed. | |||||
| */ | |||||
| print_dev_ctl(dp, ctl, true, true); | |||||
| } | |||||
| found = true; | found = true; | ||||
| break; | break; | ||||
| } | } | ||||
| TAILQ_FOREACH(ch, &dp->chans, next) { | TAILQ_FOREACH(ch, &dp->chans, next) { | ||||
| for (ctl = chan_ctls; ctl->name != NULL; ctl++) { | for (ctl = chan_ctls; ctl->name != NULL; ctl++) { | ||||
| if (strcmp(ctl->name, propstr) != 0) | if (strcmp(ctl->name, propstr) != 0) | ||||
| continue; | continue; | ||||
| print_chan_ctl(ch, ctl, true, true); | print_chan_ctl(ch, ctl, true, true); | ||||
| show = false; | show = false; | ||||
| found = true; | found = true; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (!found) | if (!found) | ||||
| warnx("%s: no such property", propstr); | xo_warnx("%s: no such property", propstr); | ||||
| next: | next: | ||||
| free(s); | free(s); | ||||
| argc--; | argc--; | ||||
| argv++; | argv++; | ||||
| } | } | ||||
| xo_close_container("executed_controls"); | |||||
| if (show) { | if (show || xo_get_style(NULL) != XO_STYLE_TEXT) { | ||||
| xo_open_list("devices"); | |||||
| print_dev(dp); | print_dev(dp); | ||||
| xo_close_list("devices"); | |||||
| } | } | ||||
| free_dev(dp); | free_dev(dp); | ||||
| xo_close_container("sndctl"); | |||||
| if (xo_finish() < 0) | |||||
| xo_err(1, "xo_finish"); | |||||
| return (0); | return (0); | ||||
| } | } | ||||