xref: /freebsd/usr.sbin/sndctl/sndctl.c (revision 30fd79b0c0a328536b166e7fa9170b059e711303)
19a37f102SChristos Margiolis /*-
29a37f102SChristos Margiolis  * SPDX-License-Identifier: BSD-2-Clause
39a37f102SChristos Margiolis  *
49a37f102SChristos Margiolis  * Copyright (c) 2024-2025 The FreeBSD Foundation
59a37f102SChristos Margiolis  *
69a37f102SChristos Margiolis  * This software was developed by Christos Margiolis <christos@FreeBSD.org>
79a37f102SChristos Margiolis  * under sponsorship from the FreeBSD Foundation.
89a37f102SChristos Margiolis  *
99a37f102SChristos Margiolis  * Redistribution and use in source and binary forms, with or without
109a37f102SChristos Margiolis  * modification, are permitted provided that the following conditions
119a37f102SChristos Margiolis  * are met:
129a37f102SChristos Margiolis  * 1. Redistributions of source code must retain the above copyright
139a37f102SChristos Margiolis  *    notice, this list of conditions and the following disclaimer.
149a37f102SChristos Margiolis  * 2. Redistributions in binary form must reproduce the above copyright
159a37f102SChristos Margiolis  *    notice, this list of conditions and the following disclaimer in the
169a37f102SChristos Margiolis  *    documentation and/or other materials provided with the distribution.
179a37f102SChristos Margiolis  *
189a37f102SChristos Margiolis  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199a37f102SChristos Margiolis  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209a37f102SChristos Margiolis  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219a37f102SChristos Margiolis  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229a37f102SChristos Margiolis  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239a37f102SChristos Margiolis  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249a37f102SChristos Margiolis  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259a37f102SChristos Margiolis  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269a37f102SChristos Margiolis  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279a37f102SChristos Margiolis  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289a37f102SChristos Margiolis  * SUCH DAMAGE.
299a37f102SChristos Margiolis  */
309a37f102SChristos Margiolis 
319a37f102SChristos Margiolis #include <sys/nv.h>
329a37f102SChristos Margiolis #include <sys/queue.h>
339a37f102SChristos Margiolis #include <sys/sndstat.h>
349a37f102SChristos Margiolis #include <sys/soundcard.h>
359a37f102SChristos Margiolis #include <sys/sysctl.h>
369a37f102SChristos Margiolis 
379a37f102SChristos Margiolis #include <err.h>
389a37f102SChristos Margiolis #include <errno.h>
399a37f102SChristos Margiolis #include <fcntl.h>
409a37f102SChristos Margiolis #include <libgen.h>
419a37f102SChristos Margiolis #include <limits.h>
429a37f102SChristos Margiolis #include <mixer.h>
439a37f102SChristos Margiolis #include <stddef.h>
449a37f102SChristos Margiolis #include <stdio.h>
459a37f102SChristos Margiolis #include <stdlib.h>
469a37f102SChristos Margiolis #include <string.h>
479a37f102SChristos Margiolis #include <unistd.h>
489a37f102SChristos Margiolis 
499a37f102SChristos Margiolis /* Taken from sys/dev/sound/pcm/ */
509a37f102SChristos Margiolis #define STATUS_LEN	64
519a37f102SChristos Margiolis #define FMTSTR_LEN	16
529a37f102SChristos Margiolis 
539a37f102SChristos Margiolis struct snd_chan {
549a37f102SChristos Margiolis 	char name[NAME_MAX];
559a37f102SChristos Margiolis 	char parentchan[NAME_MAX];
569a37f102SChristos Margiolis 	int unit;
579a37f102SChristos Margiolis #define INPUT	0
589a37f102SChristos Margiolis #define OUTPUT	1
599a37f102SChristos Margiolis 	int direction;
609a37f102SChristos Margiolis 	char caps[BUFSIZ];
619a37f102SChristos Margiolis 	int latency;
629a37f102SChristos Margiolis 	int rate;
639a37f102SChristos Margiolis 	char format[FMTSTR_LEN];
649a37f102SChristos Margiolis 	int pid;
659a37f102SChristos Margiolis 	char proc[NAME_MAX];
669a37f102SChristos Margiolis 	int interrupts;
679a37f102SChristos Margiolis 	int xruns;
689a37f102SChristos Margiolis 	int feedcount;
699a37f102SChristos Margiolis 	int volume;
709a37f102SChristos Margiolis 	struct {
719a37f102SChristos Margiolis 		char format[FMTSTR_LEN];
729a37f102SChristos Margiolis 		int rate;
739a37f102SChristos Margiolis 		int size_bytes;
749a37f102SChristos Margiolis 		int size_frames;
759a37f102SChristos Margiolis 		int blksz;
769a37f102SChristos Margiolis 		int blkcnt;
779a37f102SChristos Margiolis 		int free;
789a37f102SChristos Margiolis 		int ready;
799a37f102SChristos Margiolis 	} hwbuf, swbuf;
809a37f102SChristos Margiolis 	char feederchain[BUFSIZ];
819a37f102SChristos Margiolis 	struct snd_dev *dev;
829a37f102SChristos Margiolis 	TAILQ_ENTRY(snd_chan) next;
839a37f102SChristos Margiolis };
849a37f102SChristos Margiolis 
859a37f102SChristos Margiolis struct snd_dev {
869a37f102SChristos Margiolis 	char name[NAME_MAX];
879a37f102SChristos Margiolis 	char desc[NAME_MAX];
889a37f102SChristos Margiolis 	char status[BUFSIZ];
899a37f102SChristos Margiolis 	char devnode[NAME_MAX];
909a37f102SChristos Margiolis 	int from_user;
919a37f102SChristos Margiolis 	int unit;
929a37f102SChristos Margiolis 	char caps[BUFSIZ];
939a37f102SChristos Margiolis 	int bitperfect;
949a37f102SChristos Margiolis 	int realtime;
959a37f102SChristos Margiolis 	int autoconv;
969a37f102SChristos Margiolis 	struct {
979a37f102SChristos Margiolis 		char format[FMTSTR_LEN];
989a37f102SChristos Margiolis 		int rate;
999a37f102SChristos Margiolis 		int pchans;
1009a37f102SChristos Margiolis 		int vchans;
1019a37f102SChristos Margiolis 		int min_rate;
1029a37f102SChristos Margiolis 		int max_rate;
1039a37f102SChristos Margiolis 		int min_chans;
1049a37f102SChristos Margiolis 		int max_chans;
1059a37f102SChristos Margiolis 		char formats[BUFSIZ];
1069a37f102SChristos Margiolis 	} play, rec;
1079a37f102SChristos Margiolis 	TAILQ_HEAD(, snd_chan) chans;
1089a37f102SChristos Margiolis };
1099a37f102SChristos Margiolis 
1109a37f102SChristos Margiolis struct snd_ctl {
1119a37f102SChristos Margiolis 	const char *name;
1129a37f102SChristos Margiolis 	size_t off;
1139a37f102SChristos Margiolis #define STR	0
1149a37f102SChristos Margiolis #define NUM	1
1159a37f102SChristos Margiolis #define VOL	2
1169a37f102SChristos Margiolis #define GRP	3
1179a37f102SChristos Margiolis 	int type;
1189a37f102SChristos Margiolis 	int (*mod)(struct snd_dev *, void *);
1199a37f102SChristos Margiolis };
1209a37f102SChristos Margiolis 
1219a37f102SChristos Margiolis struct map {
1229a37f102SChristos Margiolis 	int val;
1239a37f102SChristos Margiolis 	const char *str;
1249a37f102SChristos Margiolis };
1259a37f102SChristos Margiolis 
1269a37f102SChristos Margiolis static int mod_bitperfect(struct snd_dev *, void *);
1279a37f102SChristos Margiolis static int mod_autoconv(struct snd_dev *, void *);
1289a37f102SChristos Margiolis static int mod_realtime(struct snd_dev *, void *);
1299a37f102SChristos Margiolis static int mod_play_vchans(struct snd_dev *, void *);
1309a37f102SChristos Margiolis static int mod_play_rate(struct snd_dev *, void *);
1319a37f102SChristos Margiolis static int mod_play_format(struct snd_dev *, void *);
1329a37f102SChristos Margiolis static int mod_rec_vchans(struct snd_dev *, void *);
1339a37f102SChristos Margiolis static int mod_rec_rate(struct snd_dev *, void *);
1349a37f102SChristos Margiolis static int mod_rec_format(struct snd_dev *, void *);
1359a37f102SChristos Margiolis 
1369a37f102SChristos Margiolis static struct snd_ctl dev_ctls[] = {
1379a37f102SChristos Margiolis #define F(member)	offsetof(struct snd_dev, member)
1389a37f102SChristos Margiolis 	{ "name",		F(name),		STR,	NULL },
1399a37f102SChristos Margiolis 	{ "desc",		F(desc),		STR,	NULL },
1409a37f102SChristos Margiolis 	{ "status",		F(status),		STR,	NULL },
1419a37f102SChristos Margiolis 	{ "devnode",		F(devnode),		STR,	NULL },
1429a37f102SChristos Margiolis 	{ "from_user",		F(from_user),		NUM,	NULL },
1439a37f102SChristos Margiolis 	{ "unit",		F(unit),		NUM,	NULL },
1449a37f102SChristos Margiolis 	{ "caps",		F(caps),		STR,	NULL },
1459a37f102SChristos Margiolis 	{ "bitperfect",		F(bitperfect),		NUM,	mod_bitperfect },
1469a37f102SChristos Margiolis 	{ "autoconv",		F(autoconv),		NUM,	mod_autoconv },
1479a37f102SChristos Margiolis 	{ "realtime",		F(realtime),		NUM,	mod_realtime },
1489a37f102SChristos Margiolis 	{ "play",		F(play),		GRP,	NULL },
1499a37f102SChristos Margiolis 	{ "play.format",	F(play.format),		STR,	mod_play_format },
1509a37f102SChristos Margiolis 	{ "play.rate",		F(play.rate),		NUM,	mod_play_rate },
1519a37f102SChristos Margiolis 	/*{ "play.pchans",	F(play.pchans),		NUM,	NULL },*/
1529a37f102SChristos Margiolis 	{ "play.vchans",	F(play.vchans),		NUM,	mod_play_vchans },
1539a37f102SChristos Margiolis 	{ "play.min_rate",	F(play.min_rate),	NUM,	NULL },
1549a37f102SChristos Margiolis 	{ "play.max_rate",	F(play.max_rate),	NUM,	NULL },
1559a37f102SChristos Margiolis 	{ "play.min_chans",	F(play.min_chans),	NUM,	NULL },
1569a37f102SChristos Margiolis 	{ "play.max_chans",	F(play.max_chans),	NUM,	NULL },
1579a37f102SChristos Margiolis 	{ "play.formats",	F(play.formats),	STR,	NULL },
1589a37f102SChristos Margiolis 	{ "rec",		F(rec),			GRP,	NULL },
1599a37f102SChristos Margiolis 	{ "rec.rate",		F(rec.rate),		NUM,	mod_rec_rate },
1609a37f102SChristos Margiolis 	{ "rec.format",		F(rec.format),		STR,	mod_rec_format },
1619a37f102SChristos Margiolis 	/*{ "rec.pchans",		F(rec.pchans),		NUM,	NULL },*/
1629a37f102SChristos Margiolis 	{ "rec.vchans",		F(rec.vchans),		NUM,	mod_rec_vchans },
1639a37f102SChristos Margiolis 	{ "rec.min_rate",	F(rec.min_rate),	NUM,	NULL },
1649a37f102SChristos Margiolis 	{ "rec.max_rate",	F(rec.max_rate),	NUM,	NULL },
1659a37f102SChristos Margiolis 	{ "rec.min_chans",	F(rec.min_chans),	NUM,	NULL },
1669a37f102SChristos Margiolis 	{ "rec.max_chans",	F(rec.max_chans),	NUM,	NULL },
1679a37f102SChristos Margiolis 	{ "rec.formats",	F(rec.formats),		STR,	NULL },
1689a37f102SChristos Margiolis 	{ NULL,			0,			0,	NULL }
1699a37f102SChristos Margiolis #undef F
1709a37f102SChristos Margiolis };
1719a37f102SChristos Margiolis 
1729a37f102SChristos Margiolis static struct snd_ctl chan_ctls[] = {
1739a37f102SChristos Margiolis #define F(member)	offsetof(struct snd_chan, member)
1749a37f102SChristos Margiolis 	/*{ "name",		F(name),		STR,	NULL },*/
1759a37f102SChristos Margiolis 	{ "parentchan",		F(parentchan),		STR,	NULL },
1769a37f102SChristos Margiolis 	{ "unit",		F(unit),		NUM,	NULL },
1779a37f102SChristos Margiolis 	{ "caps",		F(caps),		STR,	NULL },
1789a37f102SChristos Margiolis 	{ "latency",		F(latency),		NUM,	NULL },
1799a37f102SChristos Margiolis 	{ "rate",		F(rate),		NUM,	NULL },
1809a37f102SChristos Margiolis 	{ "format",		F(format),		STR,	NULL },
1819a37f102SChristos Margiolis 	{ "pid",		F(pid),			NUM,	NULL },
1829a37f102SChristos Margiolis 	{ "proc",		F(proc),		STR,	NULL },
1839a37f102SChristos Margiolis 	{ "interrupts",		F(interrupts),		NUM,	NULL },
1849a37f102SChristos Margiolis 	{ "xruns",		F(xruns),		NUM,	NULL },
1859a37f102SChristos Margiolis 	{ "feedcount",		F(feedcount),		NUM,	NULL },
1869a37f102SChristos Margiolis 	{ "volume",		F(volume),		VOL,	NULL },
1879a37f102SChristos Margiolis 	{ "hwbuf",		F(hwbuf),		GRP,	NULL },
1889a37f102SChristos Margiolis 	{ "hwbuf.format",	F(hwbuf.format),	STR,	NULL },
1899a37f102SChristos Margiolis 	{ "hwbuf.rate",		F(hwbuf.rate),		NUM,	NULL },
1909a37f102SChristos Margiolis 	{ "hwbuf.size_bytes",	F(hwbuf.size_bytes),	NUM,	NULL },
1919a37f102SChristos Margiolis 	{ "hwbuf.size_frames",	F(hwbuf.size_frames),	NUM,	NULL },
1929a37f102SChristos Margiolis 	{ "hwbuf.blksz",	F(hwbuf.blksz),		NUM,	NULL },
1939a37f102SChristos Margiolis 	{ "hwbuf.blkcnt",	F(hwbuf.blkcnt),	NUM,	NULL },
1949a37f102SChristos Margiolis 	{ "hwbuf.free",		F(hwbuf.free),		NUM,	NULL },
1959a37f102SChristos Margiolis 	{ "hwbuf.ready",	F(hwbuf.ready),		NUM,	NULL },
1969a37f102SChristos Margiolis 	{ "swbuf",		F(swbuf),		GRP,	NULL },
1979a37f102SChristos Margiolis 	{ "swbuf.format",	F(swbuf.format),	STR,	NULL },
1989a37f102SChristos Margiolis 	{ "swbuf.rate",		F(swbuf.rate),		NUM,	NULL },
1999a37f102SChristos Margiolis 	{ "swbuf.size_bytes",	F(swbuf.size_bytes),	NUM,	NULL },
2009a37f102SChristos Margiolis 	{ "swbuf.size_frames",	F(swbuf.size_frames),	NUM,	NULL },
2019a37f102SChristos Margiolis 	{ "swbuf.blksz",	F(swbuf.blksz),		NUM,	NULL },
2029a37f102SChristos Margiolis 	{ "swbuf.blkcnt",	F(swbuf.blkcnt),	NUM,	NULL },
2039a37f102SChristos Margiolis 	{ "swbuf.free",		F(swbuf.free),		NUM,	NULL },
2049a37f102SChristos Margiolis 	{ "swbuf.ready",	F(swbuf.ready),		NUM,	NULL },
2059a37f102SChristos Margiolis 	{ "feederchain",	F(feederchain),		STR,	NULL },
2069a37f102SChristos Margiolis 	{ NULL,			0,			0,	NULL }
2079a37f102SChristos Margiolis #undef F
2089a37f102SChristos Margiolis };
2099a37f102SChristos Margiolis 
2109a37f102SChristos Margiolis /*
2119a37f102SChristos Margiolis  * Taken from the OSSv4 manual. Not all of them are supported on FreeBSD
2129a37f102SChristos Margiolis  * however, and some of them are obsolete.
2139a37f102SChristos Margiolis  */
2149a37f102SChristos Margiolis static struct map capmap[] = {
2159a37f102SChristos Margiolis 	{ PCM_CAP_ANALOGIN,	"ANALOGIN" },
2169a37f102SChristos Margiolis 	{ PCM_CAP_ANALOGOUT,	"ANALOGOUT" },
2179a37f102SChristos Margiolis 	{ PCM_CAP_BATCH,	"BATCH" },
2189a37f102SChristos Margiolis 	{ PCM_CAP_BIND,		"BIND" },
2199a37f102SChristos Margiolis 	{ PCM_CAP_COPROC,	"COPROC" },
2209a37f102SChristos Margiolis 	{ PCM_CAP_DEFAULT,	"DEFAULT" },
2219a37f102SChristos Margiolis 	{ PCM_CAP_DIGITALIN,	"DIGITALIN" },
2229a37f102SChristos Margiolis 	{ PCM_CAP_DIGITALOUT,	"DIGITALOUT" },
2239a37f102SChristos Margiolis 	{ PCM_CAP_DUPLEX,	"DUPLEX" },
2249a37f102SChristos Margiolis 	{ PCM_CAP_FREERATE,	"FREERATE" },
2259a37f102SChristos Margiolis 	{ PCM_CAP_HIDDEN,	"HIDDEN" },
2269a37f102SChristos Margiolis 	{ PCM_CAP_INPUT,	"INPUT" },
2279a37f102SChristos Margiolis 	{ PCM_CAP_MMAP,		"MMAP" },
2289a37f102SChristos Margiolis 	{ PCM_CAP_MODEM,	"MODEM" },
2299a37f102SChristos Margiolis 	{ PCM_CAP_MULTI,	"MULTI" },
2309a37f102SChristos Margiolis 	{ PCM_CAP_OUTPUT,	"OUTPUT" },
2319a37f102SChristos Margiolis 	{ PCM_CAP_REALTIME,	"REALTIME" },
2329a37f102SChristos Margiolis 	{ PCM_CAP_REVISION,	"REVISION" },
2339a37f102SChristos Margiolis 	{ PCM_CAP_SHADOW,	"SHADOW" },
2349a37f102SChristos Margiolis 	{ PCM_CAP_SPECIAL,	"SPECIAL" },
2359a37f102SChristos Margiolis 	{ PCM_CAP_TRIGGER,	"TRIGGER" },
2369a37f102SChristos Margiolis 	{ PCM_CAP_VIRTUAL,	"VIRTUAL" },
2379a37f102SChristos Margiolis 	{ 0,			NULL }
2389a37f102SChristos Margiolis };
2399a37f102SChristos Margiolis 
2409a37f102SChristos Margiolis static struct map fmtmap[] = {
2419a37f102SChristos Margiolis 	{ AFMT_A_LAW,		"alaw" },
2429a37f102SChristos Margiolis 	{ AFMT_MU_LAW,		"mulaw" },
2439a37f102SChristos Margiolis 	{ AFMT_S8,		"s8" },
2449a37f102SChristos Margiolis 	{ AFMT_U8,		"u8" },
2459a37f102SChristos Margiolis 	{ AFMT_AC3,		"ac3" },
2469a37f102SChristos Margiolis 	{ AFMT_S16_LE,		"s16le" },
2479a37f102SChristos Margiolis 	{ AFMT_S16_BE,		"s16be" },
2489a37f102SChristos Margiolis 	{ AFMT_U16_LE,		"u16le" },
2499a37f102SChristos Margiolis 	{ AFMT_U16_BE,		"u16be" },
2509a37f102SChristos Margiolis 	{ AFMT_S24_LE,		"s24le" },
2519a37f102SChristos Margiolis 	{ AFMT_S24_BE,		"s24be" },
2529a37f102SChristos Margiolis 	{ AFMT_U24_LE,		"u24le" },
2539a37f102SChristos Margiolis 	{ AFMT_U24_BE,		"u24be" },
2549a37f102SChristos Margiolis 	{ AFMT_S32_LE,		"s32le" },
2559a37f102SChristos Margiolis 	{ AFMT_S32_BE,		"s32be" },
2569a37f102SChristos Margiolis 	{ AFMT_U32_LE,		"u32le" },
2579a37f102SChristos Margiolis 	{ AFMT_U32_BE,		"u32be" },
2589a37f102SChristos Margiolis 	{ AFMT_F32_LE,		"f32le" },
2599a37f102SChristos Margiolis 	{ AFMT_F32_BE,		"f32be" },
2609a37f102SChristos Margiolis 	{ 0,			NULL }
2619a37f102SChristos Margiolis };
2629a37f102SChristos Margiolis 
2639a37f102SChristos Margiolis static bool oflag = false;
2649a37f102SChristos Margiolis static bool vflag = false;
2659a37f102SChristos Margiolis 
2669a37f102SChristos Margiolis static void
cap2str(char * buf,size_t size,int caps)2679a37f102SChristos Margiolis cap2str(char *buf, size_t size, int caps)
2689a37f102SChristos Margiolis {
2699a37f102SChristos Margiolis 	struct map *p;
2709a37f102SChristos Margiolis 
2719a37f102SChristos Margiolis 	for (p = capmap; p->str != NULL; p++) {
2729a37f102SChristos Margiolis 		if ((p->val & caps) == 0)
2739a37f102SChristos Margiolis 			continue;
2749a37f102SChristos Margiolis 		strlcat(buf, p->str, size);
2759a37f102SChristos Margiolis 		strlcat(buf, ",", size);
2769a37f102SChristos Margiolis 	}
2779a37f102SChristos Margiolis 	if (*buf == '\0')
2789a37f102SChristos Margiolis 		strlcpy(buf, "UNKNOWN", size);
2799a37f102SChristos Margiolis 	else
2809a37f102SChristos Margiolis 		buf[strlen(buf) - 1] = '\0';
2819a37f102SChristos Margiolis }
2829a37f102SChristos Margiolis 
2839a37f102SChristos Margiolis static void
fmt2str(char * buf,size_t size,int fmt)2849a37f102SChristos Margiolis fmt2str(char *buf, size_t size, int fmt)
2859a37f102SChristos Margiolis {
2869a37f102SChristos Margiolis 	struct map *p;
2879a37f102SChristos Margiolis 	int enc, ch, ext;
2889a37f102SChristos Margiolis 
2899a37f102SChristos Margiolis 	enc = fmt & 0xf00fffff;
2909a37f102SChristos Margiolis 	ch = (fmt & 0x07f00000) >> 20;
2919a37f102SChristos Margiolis 	ext = (fmt & 0x08000000) >> 27;
2929a37f102SChristos Margiolis 
2939a37f102SChristos Margiolis 	for (p = fmtmap; p->str != NULL; p++) {
2949a37f102SChristos Margiolis 		if ((p->val & enc) == 0)
2959a37f102SChristos Margiolis 			continue;
2969a37f102SChristos Margiolis 		strlcat(buf, p->str, size);
2979a37f102SChristos Margiolis 		if (ch) {
2989a37f102SChristos Margiolis 			snprintf(buf + strlen(buf), size,
2999a37f102SChristos Margiolis 			    ":%d.%d", ch - ext, ext);
3009a37f102SChristos Margiolis 		}
3019a37f102SChristos Margiolis 		strlcat(buf, ",", size);
3029a37f102SChristos Margiolis 	}
3039a37f102SChristos Margiolis 	if (*buf == '\0')
3049a37f102SChristos Margiolis 		strlcpy(buf, "UNKNOWN", size);
3059a37f102SChristos Margiolis 	else
3069a37f102SChristos Margiolis 		buf[strlen(buf) - 1] = '\0';
3079a37f102SChristos Margiolis }
3089a37f102SChristos Margiolis 
3099a37f102SChristos Margiolis static int
bytes2frames(int bytes,int fmt)3109a37f102SChristos Margiolis bytes2frames(int bytes, int fmt)
3119a37f102SChristos Margiolis {
3129a37f102SChristos Margiolis 	int enc, ch, samplesz;
3139a37f102SChristos Margiolis 
3149a37f102SChristos Margiolis 	enc = fmt & 0xf00fffff;
3159a37f102SChristos Margiolis 	ch = (fmt & 0x07f00000) >> 20;
3169a37f102SChristos Margiolis 	/* Add the channel extension if present (e.g 2.1). */
3179a37f102SChristos Margiolis 	ch += (fmt & 0x08000000) >> 27;
3189a37f102SChristos Margiolis 
3199a37f102SChristos Margiolis 	if (enc & (AFMT_S8 | AFMT_U8 | AFMT_MU_LAW | AFMT_A_LAW))
3209a37f102SChristos Margiolis 		samplesz = 1;
3219a37f102SChristos Margiolis 	else if (enc & (AFMT_S16_NE | AFMT_U16_NE))
3229a37f102SChristos Margiolis 		samplesz = 2;
3239a37f102SChristos Margiolis 	else if (enc & (AFMT_S24_NE | AFMT_U24_NE))
3249a37f102SChristos Margiolis 		samplesz = 3;
3259a37f102SChristos Margiolis 	else if (enc & (AFMT_S32_NE | AFMT_U32_NE | AFMT_F32_NE))
3269a37f102SChristos Margiolis 		samplesz = 4;
3279a37f102SChristos Margiolis 	else
3289a37f102SChristos Margiolis 		samplesz = 0;
3299a37f102SChristos Margiolis 
3309a37f102SChristos Margiolis 	if (!samplesz || !ch)
3319a37f102SChristos Margiolis 		return (-1);
3329a37f102SChristos Margiolis 
3339a37f102SChristos Margiolis 	return (bytes / (samplesz * ch));
3349a37f102SChristos Margiolis }
3359a37f102SChristos Margiolis 
336*30fd79b0SChristos Margiolis static int
sysctl_int(const char * buf,const char * arg,int * var)337*30fd79b0SChristos Margiolis sysctl_int(const char *buf, const char *arg, int *var)
338*30fd79b0SChristos Margiolis {
339*30fd79b0SChristos Margiolis 	size_t size;
340*30fd79b0SChristos Margiolis 	int n, prev;
341*30fd79b0SChristos Margiolis 
342*30fd79b0SChristos Margiolis 	size = sizeof(int);
343*30fd79b0SChristos Margiolis 	/* Read current value. */
344*30fd79b0SChristos Margiolis 	if (sysctlbyname(buf, &prev, &size, NULL, 0) < 0) {
345*30fd79b0SChristos Margiolis 		warn("sysctlbyname(%s)", buf);
346*30fd79b0SChristos Margiolis 		return (-1);
347*30fd79b0SChristos Margiolis 	}
348*30fd79b0SChristos Margiolis 
349*30fd79b0SChristos Margiolis 	/* Read-only. */
350*30fd79b0SChristos Margiolis 	if (arg != NULL) {
351*30fd79b0SChristos Margiolis 		errno = 0;
352*30fd79b0SChristos Margiolis 		n = strtol(arg, NULL, 10);
353*30fd79b0SChristos Margiolis 		if (errno == EINVAL || errno == ERANGE) {
354*30fd79b0SChristos Margiolis 			warn("strtol(%s)", arg);
355*30fd79b0SChristos Margiolis 			return (-1);
356*30fd79b0SChristos Margiolis 		}
357*30fd79b0SChristos Margiolis 
358*30fd79b0SChristos Margiolis 		/* Apply new value. */
359*30fd79b0SChristos Margiolis 		if (sysctlbyname(buf, NULL, 0, &n, size) < 0) {
360*30fd79b0SChristos Margiolis 			warn("sysctlbyname(%s, %d)", buf, n);
361*30fd79b0SChristos Margiolis 			return (-1);
362*30fd79b0SChristos Margiolis 		}
363*30fd79b0SChristos Margiolis 	}
364*30fd79b0SChristos Margiolis 
365*30fd79b0SChristos Margiolis 	/* Read back applied value for good measure. */
366*30fd79b0SChristos Margiolis 	if (sysctlbyname(buf, &n, &size, NULL, 0) < 0) {
367*30fd79b0SChristos Margiolis 		warn("sysctlbyname(%s)", buf);
368*30fd79b0SChristos Margiolis 		return (-1);
369*30fd79b0SChristos Margiolis 	}
370*30fd79b0SChristos Margiolis 
371*30fd79b0SChristos Margiolis 	if (arg != NULL)
372*30fd79b0SChristos Margiolis 		printf("%s: %d -> %d\n", buf, prev, n);
373*30fd79b0SChristos Margiolis 	if (var != NULL)
374*30fd79b0SChristos Margiolis 		*var = n;
375*30fd79b0SChristos Margiolis 
376*30fd79b0SChristos Margiolis 	return (0);
377*30fd79b0SChristos Margiolis }
378*30fd79b0SChristos Margiolis 
379*30fd79b0SChristos Margiolis static int
sysctl_str(const char * buf,const char * arg,char * var,size_t varsz)380*30fd79b0SChristos Margiolis sysctl_str(const char *buf, const char *arg, char *var, size_t varsz)
381*30fd79b0SChristos Margiolis {
382*30fd79b0SChristos Margiolis 	size_t size;
383*30fd79b0SChristos Margiolis 	char prev[BUFSIZ];
384*30fd79b0SChristos Margiolis 	char *tmp;
385*30fd79b0SChristos Margiolis 
386*30fd79b0SChristos Margiolis 	/* Read current value. */
387*30fd79b0SChristos Margiolis 	size = sizeof(prev);
388*30fd79b0SChristos Margiolis 	if (sysctlbyname(buf, prev, &size, NULL, 0) < 0) {
389*30fd79b0SChristos Margiolis 		warn("sysctlbyname(%s)", buf);
390*30fd79b0SChristos Margiolis 		return (-1);
391*30fd79b0SChristos Margiolis 	}
392*30fd79b0SChristos Margiolis 
393*30fd79b0SChristos Margiolis 	/* Read-only. */
394*30fd79b0SChristos Margiolis 	if (arg != NULL) {
395*30fd79b0SChristos Margiolis 		size = strlen(arg);
396*30fd79b0SChristos Margiolis 		/* Apply new value. */
397*30fd79b0SChristos Margiolis 		if (sysctlbyname(buf, NULL, 0, arg, size) < 0) {
398*30fd79b0SChristos Margiolis 			warn("sysctlbyname(%s, %s)", buf, arg);
399*30fd79b0SChristos Margiolis 			return (-1);
400*30fd79b0SChristos Margiolis 		}
401*30fd79b0SChristos Margiolis 		/* Get size of new string. */
402*30fd79b0SChristos Margiolis 		if (sysctlbyname(buf, NULL, &size, NULL, 0) < 0) {
403*30fd79b0SChristos Margiolis 			warn("sysctlbyname(%s)", buf);
404*30fd79b0SChristos Margiolis 			return (-1);
405*30fd79b0SChristos Margiolis 		}
406*30fd79b0SChristos Margiolis 	}
407*30fd79b0SChristos Margiolis 
408*30fd79b0SChristos Margiolis 	if ((tmp = calloc(1, size)) == NULL)
409*30fd79b0SChristos Margiolis 		err(1, "calloc");
410*30fd79b0SChristos Margiolis 	/* Read back applied value for good measure. */
411*30fd79b0SChristos Margiolis 	if (sysctlbyname(buf, tmp, &size, NULL, 0) < 0) {
412*30fd79b0SChristos Margiolis 		warn("sysctlbyname(%s)", buf);
413*30fd79b0SChristos Margiolis 		free(tmp);
414*30fd79b0SChristos Margiolis 		return (-1);
415*30fd79b0SChristos Margiolis 	}
416*30fd79b0SChristos Margiolis 
417*30fd79b0SChristos Margiolis 	if (arg != NULL)
418*30fd79b0SChristos Margiolis 		printf("%s: %s -> %s\n", buf, prev, tmp);
419*30fd79b0SChristos Margiolis 	if (var != NULL)
420*30fd79b0SChristos Margiolis 		strlcpy(var, tmp, varsz);
421*30fd79b0SChristos Margiolis 	free(tmp);
422*30fd79b0SChristos Margiolis 
423*30fd79b0SChristos Margiolis 	return (0);
424*30fd79b0SChristos Margiolis }
425*30fd79b0SChristos Margiolis 
4269a37f102SChristos Margiolis static struct snd_dev *
read_dev(char * path)4279a37f102SChristos Margiolis read_dev(char *path)
4289a37f102SChristos Margiolis {
4299a37f102SChristos Margiolis 	nvlist_t *nvl;
4309a37f102SChristos Margiolis 	const nvlist_t * const *di;
4319a37f102SChristos Margiolis 	const nvlist_t * const *cdi;
4329a37f102SChristos Margiolis 	struct sndstioc_nv_arg arg;
4339a37f102SChristos Margiolis 	struct snd_dev *dp = NULL;
4349a37f102SChristos Margiolis 	struct snd_chan *ch;
4359a37f102SChristos Margiolis 	size_t nitems, nchans, i, j;
436*30fd79b0SChristos Margiolis 	int fd, caps, unit, t1, t2, t3;
4379a37f102SChristos Margiolis 
4389a37f102SChristos Margiolis 	if ((fd = open("/dev/sndstat", O_RDONLY)) < 0)
4399a37f102SChristos Margiolis 		err(1, "open(/dev/sndstat)");
4409a37f102SChristos Margiolis 
4419a37f102SChristos Margiolis 	if (ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL) < 0)
4429a37f102SChristos Margiolis 		err(1, "ioctl(SNDSTIOC_REFRESH_DEVS)");
4439a37f102SChristos Margiolis 
4449a37f102SChristos Margiolis 	arg.nbytes = 0;
4459a37f102SChristos Margiolis 	arg.buf = NULL;
4469a37f102SChristos Margiolis 	if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0)
4479a37f102SChristos Margiolis 		err(1, "ioctl(SNDSTIOC_GET_DEVS#1)");
4489a37f102SChristos Margiolis 
4499a37f102SChristos Margiolis 	if ((arg.buf = malloc(arg.nbytes)) == NULL)
4509a37f102SChristos Margiolis 		err(1, "malloc");
4519a37f102SChristos Margiolis 
4529a37f102SChristos Margiolis 	if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0)
4539a37f102SChristos Margiolis 		err(1, "ioctl(SNDSTIOC_GET_DEVS#2)");
4549a37f102SChristos Margiolis 
4559a37f102SChristos Margiolis 	if ((nvl = nvlist_unpack(arg.buf, arg.nbytes, 0)) == NULL)
4569a37f102SChristos Margiolis 		err(1, "nvlist_unpack");
4579a37f102SChristos Margiolis 
4589a37f102SChristos Margiolis 	if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS))
4599a37f102SChristos Margiolis 		errx(1, "no soundcards attached");
4609a37f102SChristos Margiolis 
4619a37f102SChristos Margiolis 	if (path == NULL || (path != NULL && strcmp(basename(path), "dsp") == 0))
4629a37f102SChristos Margiolis 		unit = mixer_get_dunit();
4639a37f102SChristos Margiolis 	else
4649a37f102SChristos Margiolis 		unit = -1;
4659a37f102SChristos Margiolis 
4669a37f102SChristos Margiolis 	/* Find whether the requested device exists */
4679a37f102SChristos Margiolis 	di = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems);
4689a37f102SChristos Margiolis 	for (i = 0; i < nitems; i++) {
4699a37f102SChristos Margiolis 		if (unit == -1 && strcmp(basename(path),
4709a37f102SChristos Margiolis 		    nvlist_get_string(di[i], SNDST_DSPS_DEVNODE)) == 0)
4719a37f102SChristos Margiolis 			break;
4729a37f102SChristos Margiolis 		else if (nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO) &&
4739a37f102SChristos Margiolis 		    (int)nvlist_get_number(nvlist_get_nvlist(di[i],
4749a37f102SChristos Margiolis 		    SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_UNIT) == unit)
4759a37f102SChristos Margiolis 			break;;
4769a37f102SChristos Margiolis 	}
4779a37f102SChristos Margiolis 	if (i == nitems)
4789a37f102SChristos Margiolis 		errx(1, "device not found");
4799a37f102SChristos Margiolis 
4809a37f102SChristos Margiolis #define NV(type, item)	\
4819a37f102SChristos Margiolis 	nvlist_get_ ## type (di[i], SNDST_DSPS_ ## item)
4829a37f102SChristos Margiolis 	if ((dp = calloc(1, sizeof(struct snd_dev))) == NULL)
4839a37f102SChristos Margiolis 		err(1, "calloc");
4849a37f102SChristos Margiolis 
4859a37f102SChristos Margiolis 	dp->unit = -1;
4869a37f102SChristos Margiolis 	strlcpy(dp->name, NV(string, NAMEUNIT), sizeof(dp->name));
4879a37f102SChristos Margiolis 	strlcpy(dp->desc, NV(string, DESC), sizeof(dp->desc));
4889a37f102SChristos Margiolis 	strlcpy(dp->devnode, NV(string, DEVNODE), sizeof(dp->devnode));
4899a37f102SChristos Margiolis 	dp->from_user = NV(bool, FROM_USER);
4909a37f102SChristos Margiolis 	dp->play.pchans = NV(number, PCHAN);
4919a37f102SChristos Margiolis 	dp->rec.pchans = NV(number, RCHAN);
4929a37f102SChristos Margiolis #undef NV
4939a37f102SChristos Margiolis 
4949a37f102SChristos Margiolis 	if (dp->play.pchans && !nvlist_exists(di[i], SNDST_DSPS_INFO_PLAY))
4959a37f102SChristos Margiolis 		errx(1, "%s: playback channel list empty", dp->name);
4969a37f102SChristos Margiolis 	if (dp->rec.pchans && !nvlist_exists(di[i], SNDST_DSPS_INFO_REC))
4979a37f102SChristos Margiolis 		errx(1, "%s: recording channel list empty", dp->name);
4989a37f102SChristos Margiolis 
4999a37f102SChristos Margiolis #define NV(type, mode, item)						\
5009a37f102SChristos Margiolis 	nvlist_get_ ## type (nvlist_get_nvlist(di[i],			\
5019a37f102SChristos Margiolis 	    SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item)
5029a37f102SChristos Margiolis 	if (dp->play.pchans) {
5039a37f102SChristos Margiolis 		dp->play.min_rate = NV(number, PLAY, MIN_RATE);
5049a37f102SChristos Margiolis 		dp->play.max_rate = NV(number, PLAY, MAX_RATE);
5059a37f102SChristos Margiolis 		dp->play.min_chans = NV(number, PLAY, MIN_CHN);
5069a37f102SChristos Margiolis 		dp->play.max_chans = NV(number, PLAY, MAX_CHN);
5079a37f102SChristos Margiolis 		fmt2str(dp->play.formats, sizeof(dp->play.formats),
5089a37f102SChristos Margiolis 		    NV(number, PLAY, FORMATS));
5099a37f102SChristos Margiolis 	}
5109a37f102SChristos Margiolis 	if (dp->rec.pchans) {
5119a37f102SChristos Margiolis 		dp->rec.min_rate = NV(number, REC, MIN_RATE);
5129a37f102SChristos Margiolis 		dp->rec.max_rate = NV(number, REC, MAX_RATE);
5139a37f102SChristos Margiolis 		dp->rec.min_chans = NV(number, REC, MIN_CHN);
5149a37f102SChristos Margiolis 		dp->rec.max_chans = NV(number, REC, MAX_CHN);
5159a37f102SChristos Margiolis 		fmt2str(dp->rec.formats, sizeof(dp->rec.formats),
5169a37f102SChristos Margiolis 		    NV(number, REC, FORMATS));
5179a37f102SChristos Margiolis 	}
5189a37f102SChristos Margiolis #undef NV
5199a37f102SChristos Margiolis 
5209a37f102SChristos Margiolis 	/*
5219a37f102SChristos Margiolis 	 * Skip further parsing if the provider is not sound(4), as the
5229a37f102SChristos Margiolis 	 * following code is sound(4)-specific.
5239a37f102SChristos Margiolis 	 */
5249a37f102SChristos Margiolis 	if (strcmp(nvlist_get_string(di[i], SNDST_DSPS_PROVIDER),
5259a37f102SChristos Margiolis 	    SNDST_DSPS_SOUND4_PROVIDER) != 0)
5269a37f102SChristos Margiolis 		goto done;
5279a37f102SChristos Margiolis 
5289a37f102SChristos Margiolis 	if (!nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO))
5299a37f102SChristos Margiolis 		errx(1, "%s: provider_info list empty", dp->name);
5309a37f102SChristos Margiolis 
5319a37f102SChristos Margiolis #define NV(type, item)							\
5329a37f102SChristos Margiolis 	nvlist_get_ ## type (nvlist_get_nvlist(di[i],			\
5339a37f102SChristos Margiolis 	    SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item)
5349a37f102SChristos Margiolis 	strlcpy(dp->status, NV(string, STATUS), sizeof(dp->status));
5359a37f102SChristos Margiolis 	dp->unit = NV(number, UNIT);
5369a37f102SChristos Margiolis 	dp->bitperfect = NV(bool, BITPERFECT);
5379a37f102SChristos Margiolis 	dp->play.vchans = NV(bool, PVCHAN);
5389a37f102SChristos Margiolis 	dp->play.rate = NV(number, PVCHANRATE);
5399a37f102SChristos Margiolis 	fmt2str(dp->play.format, sizeof(dp->play.format),
5409a37f102SChristos Margiolis 	    NV(number, PVCHANFORMAT));
5419a37f102SChristos Margiolis 	dp->rec.vchans = NV(bool, RVCHAN);
5429a37f102SChristos Margiolis 	dp->rec.rate = NV(number, RVCHANRATE);
5439a37f102SChristos Margiolis 	fmt2str(dp->rec.format, sizeof(dp->rec.format),
5449a37f102SChristos Margiolis 	    NV(number, RVCHANFORMAT));
5459a37f102SChristos Margiolis #undef NV
5469a37f102SChristos Margiolis 
5479a37f102SChristos Margiolis 	dp->autoconv = (dp->play.vchans || dp->rec.vchans) && !dp->bitperfect;
5489a37f102SChristos Margiolis 
549*30fd79b0SChristos Margiolis 	if (sysctl_int("hw.snd.latency", NULL, &t1) ||
550*30fd79b0SChristos Margiolis 	    sysctl_int("hw.snd.latency_profile", NULL, &t2) ||
551*30fd79b0SChristos Margiolis 	    sysctl_int("kern.timecounter.alloweddeviation", NULL, &t3))
552*30fd79b0SChristos Margiolis 		err(1, "%s: sysctl", dp->name);
553*30fd79b0SChristos Margiolis 	if (t1 == 0 && t2 == 0 && t3 == 0)
554*30fd79b0SChristos Margiolis 		dp->realtime = 1;
555*30fd79b0SChristos Margiolis 
5569a37f102SChristos Margiolis 	if (!nvlist_exists(nvlist_get_nvlist(di[i],
5579a37f102SChristos Margiolis 	    SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_CHAN_INFO))
5589a37f102SChristos Margiolis 		errx(1, "%s: channel info list empty", dp->name);
5599a37f102SChristos Margiolis 
5609a37f102SChristos Margiolis 	cdi = nvlist_get_nvlist_array(
5619a37f102SChristos Margiolis 	    nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO),
5629a37f102SChristos Margiolis 	    SNDST_DSPS_SOUND4_CHAN_INFO, &nchans);
5639a37f102SChristos Margiolis 
5649a37f102SChristos Margiolis 	TAILQ_INIT(&dp->chans);
5659a37f102SChristos Margiolis 	caps = 0;
5669a37f102SChristos Margiolis 	for (j = 0; j < nchans; j++) {
5679a37f102SChristos Margiolis #define NV(type, item)	\
5689a37f102SChristos Margiolis 	nvlist_get_ ## type (cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item)
5699a37f102SChristos Margiolis 		if ((ch = calloc(1, sizeof(struct snd_chan))) == NULL)
5709a37f102SChristos Margiolis 			err(1, "calloc");
5719a37f102SChristos Margiolis 
5729a37f102SChristos Margiolis 		strlcpy(ch->name, NV(string, NAME), sizeof(ch->name));
5739a37f102SChristos Margiolis 		strlcpy(ch->parentchan, NV(string, PARENTCHAN),
5749a37f102SChristos Margiolis 		    sizeof(ch->parentchan));
5759a37f102SChristos Margiolis 		ch->unit = NV(number, UNIT);
5769a37f102SChristos Margiolis 		ch->direction = (NV(number, CAPS) & PCM_CAP_INPUT) ?
5779a37f102SChristos Margiolis 		    INPUT : OUTPUT;
5789a37f102SChristos Margiolis 		cap2str(ch->caps, sizeof(ch->caps), NV(number, CAPS));
5799a37f102SChristos Margiolis 		ch->latency = NV(number, LATENCY);
5809a37f102SChristos Margiolis 		ch->rate = NV(number, RATE);
5819a37f102SChristos Margiolis 		fmt2str(ch->format, sizeof(ch->format), NV(number, FORMAT));
5829a37f102SChristos Margiolis 		ch->pid = NV(number, PID);
5839a37f102SChristos Margiolis 		strlcpy(ch->proc, NV(string, COMM), sizeof(ch->proc));
5849a37f102SChristos Margiolis 		ch->interrupts = NV(number, INTR);
5859a37f102SChristos Margiolis 		ch->xruns = NV(number, XRUNS);
5869a37f102SChristos Margiolis 		ch->feedcount = NV(number, FEEDCNT);
5879a37f102SChristos Margiolis 		ch->volume = NV(number, LEFTVOL) |
5889a37f102SChristos Margiolis 		    NV(number, RIGHTVOL) << 8;
5899a37f102SChristos Margiolis 		fmt2str(ch->hwbuf.format, sizeof(ch->hwbuf.format),
5909a37f102SChristos Margiolis 		    NV(number, HWBUF_FORMAT));
5919a37f102SChristos Margiolis 		ch->hwbuf.rate = NV(number, HWBUF_RATE);
5929a37f102SChristos Margiolis 		ch->hwbuf.size_bytes = NV(number, HWBUF_SIZE);
5939a37f102SChristos Margiolis 		ch->hwbuf.size_frames =
5949a37f102SChristos Margiolis 		    bytes2frames(ch->hwbuf.size_bytes, NV(number, HWBUF_FORMAT));
5959a37f102SChristos Margiolis 		ch->hwbuf.blksz = NV(number, HWBUF_BLKSZ);
5969a37f102SChristos Margiolis 		ch->hwbuf.blkcnt = NV(number, HWBUF_BLKCNT);
5979a37f102SChristos Margiolis 		ch->hwbuf.free = NV(number, HWBUF_FREE);
5989a37f102SChristos Margiolis 		ch->hwbuf.ready = NV(number, HWBUF_READY);
5999a37f102SChristos Margiolis 		fmt2str(ch->swbuf.format, sizeof(ch->swbuf.format),
6009a37f102SChristos Margiolis 		    NV(number, SWBUF_FORMAT));
6019a37f102SChristos Margiolis 		ch->swbuf.rate = NV(number, SWBUF_RATE);
6029a37f102SChristos Margiolis 		ch->swbuf.size_bytes = NV(number, SWBUF_SIZE);
6039a37f102SChristos Margiolis 		ch->swbuf.size_frames =
6049a37f102SChristos Margiolis 		    bytes2frames(ch->swbuf.size_bytes, NV(number, SWBUF_FORMAT));
6059a37f102SChristos Margiolis 		ch->swbuf.blksz = NV(number, SWBUF_BLKSZ);
6069a37f102SChristos Margiolis 		ch->swbuf.blkcnt = NV(number, SWBUF_BLKCNT);
6079a37f102SChristos Margiolis 		ch->swbuf.free = NV(number, SWBUF_FREE);
6089a37f102SChristos Margiolis 		ch->swbuf.ready = NV(number, SWBUF_READY);
6099a37f102SChristos Margiolis 		strlcpy(ch->feederchain, NV(string, FEEDERCHAIN),
6109a37f102SChristos Margiolis 		    sizeof(ch->feederchain));
6119a37f102SChristos Margiolis 		ch->dev = dp;
6129a37f102SChristos Margiolis 
6139a37f102SChristos Margiolis 		caps |= NV(number, CAPS);
6149a37f102SChristos Margiolis 		TAILQ_INSERT_TAIL(&dp->chans, ch, next);
6159a37f102SChristos Margiolis 
6169a37f102SChristos Margiolis 		if (!dp->rec.vchans && ch->direction == INPUT) {
6179a37f102SChristos Margiolis 			strlcpy(dp->rec.format, ch->hwbuf.format,
6189a37f102SChristos Margiolis 			    sizeof(dp->rec.format));
6199a37f102SChristos Margiolis 			dp->rec.rate = ch->hwbuf.rate;
6209a37f102SChristos Margiolis 		} else if (!dp->play.vchans && ch->direction == OUTPUT) {
6219a37f102SChristos Margiolis 			strlcpy(dp->play.format, ch->hwbuf.format,
6229a37f102SChristos Margiolis 			    sizeof(dp->play.format));
6239a37f102SChristos Margiolis 			dp->play.rate = ch->hwbuf.rate;
6249a37f102SChristos Margiolis 		}
6259a37f102SChristos Margiolis #undef NV
6269a37f102SChristos Margiolis 	}
6279a37f102SChristos Margiolis 	cap2str(dp->caps, sizeof(dp->caps), caps);
6289a37f102SChristos Margiolis 
6299a37f102SChristos Margiolis done:
6309a37f102SChristos Margiolis 	free(arg.buf);
6319a37f102SChristos Margiolis 	nvlist_destroy(nvl);
6329a37f102SChristos Margiolis 	close(fd);
6339a37f102SChristos Margiolis 
6349a37f102SChristos Margiolis 	return (dp);
6359a37f102SChristos Margiolis }
6369a37f102SChristos Margiolis 
6379a37f102SChristos Margiolis static void
free_dev(struct snd_dev * dp)6389a37f102SChristos Margiolis free_dev(struct snd_dev *dp)
6399a37f102SChristos Margiolis {
6409a37f102SChristos Margiolis 	struct snd_chan *ch;
6419a37f102SChristos Margiolis 
6429a37f102SChristos Margiolis 	while (!TAILQ_EMPTY(&dp->chans)) {
6439a37f102SChristos Margiolis 		ch = TAILQ_FIRST(&dp->chans);
6449a37f102SChristos Margiolis 		TAILQ_REMOVE(&dp->chans, ch, next);
6459a37f102SChristos Margiolis 		free(ch);
6469a37f102SChristos Margiolis 	}
6479a37f102SChristos Margiolis 	free(dp);
6489a37f102SChristos Margiolis }
6499a37f102SChristos Margiolis 
6509a37f102SChristos Margiolis static void
print_dev_ctl(struct snd_dev * dp,struct snd_ctl * ctl,bool simple,bool showgrp)6519a37f102SChristos Margiolis print_dev_ctl(struct snd_dev *dp, struct snd_ctl *ctl, bool simple,
6529a37f102SChristos Margiolis     bool showgrp)
6539a37f102SChristos Margiolis {
6549a37f102SChristos Margiolis 	struct snd_ctl *cp;
6559a37f102SChristos Margiolis 	size_t len;
6569a37f102SChristos Margiolis 
6579a37f102SChristos Margiolis 	if (ctl->type != GRP) {
6589a37f102SChristos Margiolis 		if (simple)
6599a37f102SChristos Margiolis 			printf("%s=", ctl->name);
6609a37f102SChristos Margiolis 		else
6619a37f102SChristos Margiolis 			printf("    %-20s= ", ctl->name);
6629a37f102SChristos Margiolis 	}
6639a37f102SChristos Margiolis 
6649a37f102SChristos Margiolis 	switch (ctl->type) {
6659a37f102SChristos Margiolis 	case STR:
6669a37f102SChristos Margiolis 		printf("%s\n", (char *)dp + ctl->off);
6679a37f102SChristos Margiolis 		break;
6689a37f102SChristos Margiolis 	case NUM:
6699a37f102SChristos Margiolis 		printf("%d\n", *(int *)((intptr_t)dp + ctl->off));
6709a37f102SChristos Margiolis 		break;
6719a37f102SChristos Margiolis 	case VOL:
6729a37f102SChristos Margiolis 		break;
6739a37f102SChristos Margiolis 	case GRP:
6749a37f102SChristos Margiolis 		if (!simple || !showgrp)
6759a37f102SChristos Margiolis 			break;
6769a37f102SChristos Margiolis 		for (cp = dev_ctls; cp->name != NULL; cp++) {
6779a37f102SChristos Margiolis 			len = strlen(ctl->name);
6789a37f102SChristos Margiolis 			if (strncmp(ctl->name, cp->name, len) == 0 &&
6799a37f102SChristos Margiolis 			    cp->name[len] == '.' && cp->type != GRP)
6809a37f102SChristos Margiolis 				print_dev_ctl(dp, cp, simple, showgrp);
6819a37f102SChristos Margiolis 		}
6829a37f102SChristos Margiolis 		break;
6839a37f102SChristos Margiolis 	}
6849a37f102SChristos Margiolis }
6859a37f102SChristos Margiolis 
6869a37f102SChristos Margiolis static void
print_chan_ctl(struct snd_chan * ch,struct snd_ctl * ctl,bool simple,bool showgrp)6879a37f102SChristos Margiolis print_chan_ctl(struct snd_chan *ch, struct snd_ctl *ctl, bool simple,
6889a37f102SChristos Margiolis     bool showgrp)
6899a37f102SChristos Margiolis {
6909a37f102SChristos Margiolis 	struct snd_ctl *cp;
6919a37f102SChristos Margiolis 	size_t len;
6929a37f102SChristos Margiolis 	int v;
6939a37f102SChristos Margiolis 
6949a37f102SChristos Margiolis 	if (ctl->type != GRP) {
6959a37f102SChristos Margiolis 		if (simple)
6969a37f102SChristos Margiolis 			printf("%s.%s=", ch->name, ctl->name);
6979a37f102SChristos Margiolis 		else
6989a37f102SChristos Margiolis 			printf("        %-20s= ", ctl->name);
6999a37f102SChristos Margiolis 	}
7009a37f102SChristos Margiolis 
7019a37f102SChristos Margiolis 	switch (ctl->type) {
7029a37f102SChristos Margiolis 	case STR:
7039a37f102SChristos Margiolis 		printf("%s\n", (char *)ch + ctl->off);
7049a37f102SChristos Margiolis 		break;
7059a37f102SChristos Margiolis 	case NUM:
7069a37f102SChristos Margiolis 		printf("%d\n", *(int *)((intptr_t)ch + ctl->off));
7079a37f102SChristos Margiolis 		break;
7089a37f102SChristos Margiolis 	case VOL:
7099a37f102SChristos Margiolis 		v = *(int *)((intptr_t)ch + ctl->off);
7109a37f102SChristos Margiolis 		printf("%.2f:%.2f\n",
7119a37f102SChristos Margiolis 		    MIX_VOLNORM(v & 0x00ff), MIX_VOLNORM((v >> 8) & 0x00ff));
7129a37f102SChristos Margiolis 		break;
7139a37f102SChristos Margiolis 	case GRP:
7149a37f102SChristos Margiolis 		if (!simple || !showgrp)
7159a37f102SChristos Margiolis 			break;
7169a37f102SChristos Margiolis 		for (cp = chan_ctls; cp->name != NULL; cp++) {
7179a37f102SChristos Margiolis 			len = strlen(ctl->name);
7189a37f102SChristos Margiolis 			if (strncmp(ctl->name, cp->name, len) == 0 &&
7199a37f102SChristos Margiolis 			    cp->name[len] == '.' && cp->type != GRP)
7209a37f102SChristos Margiolis 				print_chan_ctl(ch, cp, simple, showgrp);
7219a37f102SChristos Margiolis 		}
7229a37f102SChristos Margiolis 		break;
7239a37f102SChristos Margiolis 	}
7249a37f102SChristos Margiolis }
7259a37f102SChristos Margiolis 
7269a37f102SChristos Margiolis static void
print_dev(struct snd_dev * dp)7279a37f102SChristos Margiolis print_dev(struct snd_dev *dp)
7289a37f102SChristos Margiolis {
7299a37f102SChristos Margiolis 	struct snd_chan *ch;
7309a37f102SChristos Margiolis 	struct snd_ctl *ctl;
7319a37f102SChristos Margiolis 
7329a37f102SChristos Margiolis 	if (!oflag) {
7339a37f102SChristos Margiolis 		printf("%s: <%s> %s", dp->name, dp->desc, dp->status);
7349a37f102SChristos Margiolis 
7359a37f102SChristos Margiolis 		printf(" (");
7369a37f102SChristos Margiolis 		if (dp->play.pchans)
7379a37f102SChristos Margiolis 			printf("play");
7389a37f102SChristos Margiolis 		if (dp->play.pchans && dp->rec.pchans)
7399a37f102SChristos Margiolis 			printf("/");
7409a37f102SChristos Margiolis 		if (dp->rec.pchans)
7419a37f102SChristos Margiolis 			printf("rec");
7429a37f102SChristos Margiolis 		printf(")\n");
7439a37f102SChristos Margiolis 	}
7449a37f102SChristos Margiolis 
7459a37f102SChristos Margiolis 	for (ctl = dev_ctls; ctl->name != NULL; ctl++)
7469a37f102SChristos Margiolis 		print_dev_ctl(dp, ctl, oflag, false);
7479a37f102SChristos Margiolis 
7489a37f102SChristos Margiolis 	if (vflag) {
7499a37f102SChristos Margiolis 		TAILQ_FOREACH(ch, &dp->chans, next) {
7509a37f102SChristos Margiolis 			if (!oflag)
7519a37f102SChristos Margiolis 				printf("    %s\n", ch->name);
7529a37f102SChristos Margiolis 			for (ctl = chan_ctls; ctl->name != NULL; ctl++)
7539a37f102SChristos Margiolis 				print_chan_ctl(ch, ctl, oflag, false);
7549a37f102SChristos Margiolis 		}
7559a37f102SChristos Margiolis 	}
7569a37f102SChristos Margiolis }
7579a37f102SChristos Margiolis 
7589a37f102SChristos Margiolis static int
mod_bitperfect(struct snd_dev * dp,void * arg)7599a37f102SChristos Margiolis mod_bitperfect(struct snd_dev *dp, void *arg)
7609a37f102SChristos Margiolis {
7619a37f102SChristos Margiolis 	char buf[64];
7629a37f102SChristos Margiolis 
7639a37f102SChristos Margiolis 	if (dp->from_user)
7649a37f102SChristos Margiolis 		return (-1);
7659a37f102SChristos Margiolis 
7669a37f102SChristos Margiolis 	snprintf(buf, sizeof(buf), "dev.pcm.%d.bitperfect", dp->unit);
7679a37f102SChristos Margiolis 
7689a37f102SChristos Margiolis 	return (sysctl_int(buf, arg, &dp->bitperfect));
7699a37f102SChristos Margiolis }
7709a37f102SChristos Margiolis 
7719a37f102SChristos Margiolis static int
mod_autoconv(struct snd_dev * dp,void * arg)7729a37f102SChristos Margiolis mod_autoconv(struct snd_dev *dp, void *arg)
7739a37f102SChristos Margiolis {
7749a37f102SChristos Margiolis 	const char *val = arg;
7759a37f102SChristos Margiolis 	const char *zero = "0";
7769a37f102SChristos Margiolis 	const char *one = "1";
7779a37f102SChristos Margiolis 	int rc = -1;
7789a37f102SChristos Margiolis 
7799a37f102SChristos Margiolis 	if (dp->from_user)
7809a37f102SChristos Margiolis 		return (rc);
7819a37f102SChristos Margiolis 
7829a37f102SChristos Margiolis 	if (strcmp(val, zero) == 0) {
7839a37f102SChristos Margiolis 		rc = mod_play_vchans(dp, __DECONST(char *, zero)) ||
7849a37f102SChristos Margiolis 		    mod_rec_vchans(dp, __DECONST(char *, zero)) ||
7859a37f102SChristos Margiolis 		    mod_bitperfect(dp, __DECONST(char *, one));
7869a37f102SChristos Margiolis 		if (rc == 0)
7879a37f102SChristos Margiolis 			dp->autoconv = 0;
7889a37f102SChristos Margiolis 	} else if (strcmp(val, one) == 0) {
7899a37f102SChristos Margiolis 		rc = mod_play_vchans(dp, __DECONST(char *, one)) ||
7909a37f102SChristos Margiolis 		    mod_rec_vchans(dp, __DECONST(char *, one)) ||
7919a37f102SChristos Margiolis 		    mod_bitperfect(dp, __DECONST(char *, zero));
7929a37f102SChristos Margiolis 		if (rc == 0)
7939a37f102SChristos Margiolis 			dp->autoconv = 1;
7949a37f102SChristos Margiolis 	}
7959a37f102SChristos Margiolis 
7969a37f102SChristos Margiolis 	return (rc);
7979a37f102SChristos Margiolis }
7989a37f102SChristos Margiolis 
7999a37f102SChristos Margiolis static int
mod_realtime(struct snd_dev * dp,void * arg)8009a37f102SChristos Margiolis mod_realtime(struct snd_dev *dp, void *arg)
8019a37f102SChristos Margiolis {
8029a37f102SChristos Margiolis 	const char *val = arg;
8039a37f102SChristos Margiolis 	int rc = -1;
8049a37f102SChristos Margiolis 
8059a37f102SChristos Margiolis 	if (dp->from_user)
8069a37f102SChristos Margiolis 		return (-1);
8079a37f102SChristos Margiolis 
8089a37f102SChristos Margiolis 	if (strcmp(val, "0") == 0) {
8099a37f102SChristos Margiolis 		/* TODO */
8109a37f102SChristos Margiolis 		rc = sysctl_int("hw.snd.latency", "2", NULL) ||
8119a37f102SChristos Margiolis 		    sysctl_int("hw.snd.latency_profile", "1", NULL) ||
8129a37f102SChristos Margiolis 		    sysctl_int("kern.timecounter.alloweddeviation", "5", NULL);
8139a37f102SChristos Margiolis 		if (rc == 0)
8149a37f102SChristos Margiolis 			dp->realtime = 0;
8159a37f102SChristos Margiolis 	} else if (strcmp(val, "1") == 0) {
8169a37f102SChristos Margiolis 		rc = sysctl_int("hw.snd.latency", "0", NULL) ||
8179a37f102SChristos Margiolis 		    sysctl_int("hw.snd.latency_profile", "0", NULL) ||
8189a37f102SChristos Margiolis 		    sysctl_int("kern.timecounter.alloweddeviation", "0", NULL);
8199a37f102SChristos Margiolis 		if (rc == 0)
8209a37f102SChristos Margiolis 			dp->realtime = 1;
8219a37f102SChristos Margiolis 	}
8229a37f102SChristos Margiolis 
8239a37f102SChristos Margiolis 	return (rc);
8249a37f102SChristos Margiolis }
8259a37f102SChristos Margiolis 
8269a37f102SChristos Margiolis static int
mod_play_vchans(struct snd_dev * dp,void * arg)8279a37f102SChristos Margiolis mod_play_vchans(struct snd_dev *dp, void *arg)
8289a37f102SChristos Margiolis {
8299a37f102SChristos Margiolis 	char buf[64];
8309a37f102SChristos Margiolis 
8319a37f102SChristos Margiolis 	if (dp->from_user)
8329a37f102SChristos Margiolis 		return (-1);
8339a37f102SChristos Margiolis 
8349a37f102SChristos Margiolis 	snprintf(buf, sizeof(buf), "dev.pcm.%d.play.vchans", dp->unit);
8359a37f102SChristos Margiolis 
8369a37f102SChristos Margiolis 	return (sysctl_int(buf, arg, &dp->play.vchans));
8379a37f102SChristos Margiolis }
8389a37f102SChristos Margiolis 
8399a37f102SChristos Margiolis static int
mod_play_rate(struct snd_dev * dp,void * arg)8409a37f102SChristos Margiolis mod_play_rate(struct snd_dev *dp, void *arg)
8419a37f102SChristos Margiolis {
8429a37f102SChristos Margiolis 	char buf[64];
8439a37f102SChristos Margiolis 
8449a37f102SChristos Margiolis 	if (dp->from_user)
8459a37f102SChristos Margiolis 		return (-1);
8469a37f102SChristos Margiolis 	if (!dp->play.vchans)
8479a37f102SChristos Margiolis 		return (0);
8489a37f102SChristos Margiolis 
8499a37f102SChristos Margiolis 	snprintf(buf, sizeof(buf), "dev.pcm.%d.play.vchanrate", dp->unit);
8509a37f102SChristos Margiolis 
8519a37f102SChristos Margiolis 	return (sysctl_int(buf, arg, &dp->play.rate));
8529a37f102SChristos Margiolis }
8539a37f102SChristos Margiolis 
8549a37f102SChristos Margiolis static int
mod_play_format(struct snd_dev * dp,void * arg)8559a37f102SChristos Margiolis mod_play_format(struct snd_dev *dp, void *arg)
8569a37f102SChristos Margiolis {
8579a37f102SChristos Margiolis 	char buf[64];
8589a37f102SChristos Margiolis 
8599a37f102SChristos Margiolis 	if (dp->from_user)
8609a37f102SChristos Margiolis 		return (-1);
8619a37f102SChristos Margiolis 	if (!dp->play.vchans)
8629a37f102SChristos Margiolis 		return (0);
8639a37f102SChristos Margiolis 
8649a37f102SChristos Margiolis 	snprintf(buf, sizeof(buf), "dev.pcm.%d.play.vchanformat", dp->unit);
8659a37f102SChristos Margiolis 
8669a37f102SChristos Margiolis 	return (sysctl_str(buf, arg, dp->play.format, sizeof(dp->play.format)));
8679a37f102SChristos Margiolis }
8689a37f102SChristos Margiolis 
8699a37f102SChristos Margiolis static int
mod_rec_vchans(struct snd_dev * dp,void * arg)8709a37f102SChristos Margiolis mod_rec_vchans(struct snd_dev *dp, void *arg)
8719a37f102SChristos Margiolis {
8729a37f102SChristos Margiolis 	char buf[64];
8739a37f102SChristos Margiolis 
8749a37f102SChristos Margiolis 	if (dp->from_user)
8759a37f102SChristos Margiolis 		return (-1);
8769a37f102SChristos Margiolis 
8779a37f102SChristos Margiolis 	snprintf(buf, sizeof(buf), "dev.pcm.%d.rec.vchans", dp->unit);
8789a37f102SChristos Margiolis 
8799a37f102SChristos Margiolis 	return (sysctl_int(buf, arg, &dp->rec.vchans));
8809a37f102SChristos Margiolis }
8819a37f102SChristos Margiolis 
8829a37f102SChristos Margiolis static int
mod_rec_rate(struct snd_dev * dp,void * arg)8839a37f102SChristos Margiolis mod_rec_rate(struct snd_dev *dp, void *arg)
8849a37f102SChristos Margiolis {
8859a37f102SChristos Margiolis 	char buf[64];
8869a37f102SChristos Margiolis 
8879a37f102SChristos Margiolis 	if (dp->from_user)
8889a37f102SChristos Margiolis 		return (-1);
8899a37f102SChristos Margiolis 	if (!dp->rec.vchans)
8909a37f102SChristos Margiolis 		return (0);
8919a37f102SChristos Margiolis 
8929a37f102SChristos Margiolis 	snprintf(buf, sizeof(buf), "dev.pcm.%d.rec.vchanrate", dp->unit);
8939a37f102SChristos Margiolis 
8949a37f102SChristos Margiolis 	return (sysctl_int(buf, arg, &dp->rec.rate));
8959a37f102SChristos Margiolis }
8969a37f102SChristos Margiolis 
8979a37f102SChristos Margiolis static int
mod_rec_format(struct snd_dev * dp,void * arg)8989a37f102SChristos Margiolis mod_rec_format(struct snd_dev *dp, void *arg)
8999a37f102SChristos Margiolis {
9009a37f102SChristos Margiolis 	char buf[64];
9019a37f102SChristos Margiolis 
9029a37f102SChristos Margiolis 	if (dp->from_user)
9039a37f102SChristos Margiolis 		return (-1);
9049a37f102SChristos Margiolis 	if (!dp->rec.vchans)
9059a37f102SChristos Margiolis 		return (0);
9069a37f102SChristos Margiolis 
9079a37f102SChristos Margiolis 	snprintf(buf, sizeof(buf), "dev.pcm.%d.rec.vchanformat", dp->unit);
9089a37f102SChristos Margiolis 
9099a37f102SChristos Margiolis 	return (sysctl_str(buf, arg, dp->rec.format, sizeof(dp->rec.format)));
9109a37f102SChristos Margiolis }
9119a37f102SChristos Margiolis 
9129a37f102SChristos Margiolis static void __dead2
usage(void)9139a37f102SChristos Margiolis usage(void)
9149a37f102SChristos Margiolis {
9159a37f102SChristos Margiolis 	fprintf(stderr, "usage: %s [-f device] [-hov] [control[=value] ...]\n",
9169a37f102SChristos Margiolis 	    getprogname());
9179a37f102SChristos Margiolis 	exit(1);
9189a37f102SChristos Margiolis }
9199a37f102SChristos Margiolis 
9209a37f102SChristos Margiolis int
main(int argc,char * argv[])9219a37f102SChristos Margiolis main(int argc, char *argv[])
9229a37f102SChristos Margiolis {
9239a37f102SChristos Margiolis 	struct snd_dev *dp;
9249a37f102SChristos Margiolis 	struct snd_chan *ch;
9259a37f102SChristos Margiolis 	struct snd_ctl *ctl;
9269a37f102SChristos Margiolis 	char *path = NULL;
9279a37f102SChristos Margiolis 	char *s, *propstr;
9289a37f102SChristos Margiolis 	bool show = true, found;
9299a37f102SChristos Margiolis 	int c;
9309a37f102SChristos Margiolis 
9319a37f102SChristos Margiolis 	while ((c = getopt(argc, argv, "f:hov")) != -1) {
9329a37f102SChristos Margiolis 		switch (c) {
9339a37f102SChristos Margiolis 		case 'f':
9349a37f102SChristos Margiolis 			path = optarg;
9359a37f102SChristos Margiolis 			break;
9369a37f102SChristos Margiolis 		case 'o':
9379a37f102SChristos Margiolis 			oflag = true;
9389a37f102SChristos Margiolis 			break;
9399a37f102SChristos Margiolis 		case 'v':
9409a37f102SChristos Margiolis 			vflag = true;
9419a37f102SChristos Margiolis 			break;
9429a37f102SChristos Margiolis 		case 'h':	/* FALLTHROUGH */
9439a37f102SChristos Margiolis 		case '?':
9449a37f102SChristos Margiolis 		default:
9459a37f102SChristos Margiolis 			usage();
9469a37f102SChristos Margiolis 		}
9479a37f102SChristos Margiolis 	}
9489a37f102SChristos Margiolis 	argc -= optind;
9499a37f102SChristos Margiolis 	argv += optind;
9509a37f102SChristos Margiolis 
9519a37f102SChristos Margiolis 	dp = read_dev(path);
9529a37f102SChristos Margiolis 
9539a37f102SChristos Margiolis 	while (argc > 0) {
9549a37f102SChristos Margiolis 		if ((s = strdup(*argv)) == NULL)
9559a37f102SChristos Margiolis 			err(1, "strdup(%s)", *argv);
9569a37f102SChristos Margiolis 
9579a37f102SChristos Margiolis 		propstr = strsep(&s, "=");
9589a37f102SChristos Margiolis 		if (propstr == NULL)
9599a37f102SChristos Margiolis 			goto next;
9609a37f102SChristos Margiolis 
9619a37f102SChristos Margiolis 		found = false;
9629a37f102SChristos Margiolis 		for (ctl = dev_ctls; ctl->name != NULL; ctl++) {
9639a37f102SChristos Margiolis 			if (strcmp(ctl->name, propstr) != 0)
9649a37f102SChristos Margiolis 				continue;
9659a37f102SChristos Margiolis 			if (s == NULL) {
9669a37f102SChristos Margiolis 				print_dev_ctl(dp, ctl, true, true);
9679a37f102SChristos Margiolis 				show = false;
9689a37f102SChristos Margiolis 			} else if (ctl->mod != NULL && ctl->mod(dp, s) < 0)
9699a37f102SChristos Margiolis 				warnx("%s(%s) failed", ctl->name, s);
9709a37f102SChristos Margiolis 			found = true;
9719a37f102SChristos Margiolis 			break;
9729a37f102SChristos Margiolis 		}
9739a37f102SChristos Margiolis 		TAILQ_FOREACH(ch, &dp->chans, next) {
9749a37f102SChristos Margiolis 			for (ctl = chan_ctls; ctl->name != NULL; ctl++) {
9759a37f102SChristos Margiolis 				if (strcmp(ctl->name, propstr) != 0)
9769a37f102SChristos Margiolis 					continue;
9779a37f102SChristos Margiolis 				print_chan_ctl(ch, ctl, true, true);
9789a37f102SChristos Margiolis 				show = false;
9799a37f102SChristos Margiolis 				found = true;
9809a37f102SChristos Margiolis 				break;
9819a37f102SChristos Margiolis 			}
9829a37f102SChristos Margiolis 		}
9839a37f102SChristos Margiolis 		if (!found)
9849a37f102SChristos Margiolis 			warnx("%s: no such property", propstr);
9859a37f102SChristos Margiolis next:
9869a37f102SChristos Margiolis 		free(s);
9879a37f102SChristos Margiolis 		argc--;
9889a37f102SChristos Margiolis 		argv++;
9899a37f102SChristos Margiolis 	}
9909a37f102SChristos Margiolis 
9919a37f102SChristos Margiolis 	free_dev(dp);
9929a37f102SChristos Margiolis 
9939a37f102SChristos Margiolis 	if (show) {
9949a37f102SChristos Margiolis 		/*
9959a37f102SChristos Margiolis 		 * Re-read dev to reflect new state in case we changed some
9969a37f102SChristos Margiolis 		 * property.
9979a37f102SChristos Margiolis 		 */
9989a37f102SChristos Margiolis 		dp = read_dev(path);
9999a37f102SChristos Margiolis 		print_dev(dp);
10009a37f102SChristos Margiolis 		free_dev(dp);
10019a37f102SChristos Margiolis 	}
10029a37f102SChristos Margiolis 
10039a37f102SChristos Margiolis 	return (0);
10049a37f102SChristos Margiolis }
1005