xref: /freebsd/tests/sys/sound/sndstat.c (revision 6e744de1a3dc5dde8d2ee51e97a1224a01bdfb21)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2024 The FreeBSD Foundation
5  *
6  * This software was developed by Christos Margiolis <christos@FreeBSD.org>
7  * under sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/linker.h>
33 #include <sys/sndstat.h>
34 #include <sys/nv.h>
35 
36 #include <atf-c.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 
42 static void
43 load_dummy(void)
44 {
45 	if (kldload("snd_dummy.ko") < 0 && errno != EEXIST)
46 		atf_tc_skip("snd_dummy.ko not found");
47 }
48 
49 ATF_TC(sndstat_nv);
50 ATF_TC_HEAD(sndstat_nv, tc)
51 {
52 	atf_tc_set_md_var(tc, "descr", "/dev/sndstat nvlist test");
53 }
54 
55 ATF_TC_BODY(sndstat_nv, tc)
56 {
57 	nvlist_t *nvl;
58 	const nvlist_t * const *di;
59 	const nvlist_t * const *cdi;
60 	struct sndstioc_nv_arg arg;
61 	size_t nitems, nchans, i, j;
62 	int fd, rc;
63 	int pchan, rchan;
64 
65 	load_dummy();
66 
67 	if ((fd = open("/dev/sndstat", O_RDONLY)) < 0)
68 		atf_tc_skip("/dev/sndstat not found, load sound(4)");
69 
70 	rc = ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL);
71 	ATF_REQUIRE_EQ(rc, 0);
72 
73 	arg.nbytes = 0;
74 	arg.buf = NULL;
75 	rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg);
76 	ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#1) failed");
77 
78 	arg.buf = malloc(arg.nbytes);
79 	ATF_REQUIRE(arg.buf != NULL);
80 
81 	rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg);
82 	ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#2) failed");
83 
84 	nvl = nvlist_unpack(arg.buf, arg.nbytes, 0);
85 	ATF_REQUIRE(nvl != NULL);
86 
87 	if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS))
88 		atf_tc_skip("no soundcards attached");
89 
90 	di = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems);
91 	for (i = 0; i < nitems; i++) {
92 #define NV(type, item)	do {						\
93 	ATF_REQUIRE_MSG(nvlist_exists(di[i], SNDST_DSPS_ ## item),	\
94 	    "SNDST_DSPS_" #item " does not exist");			\
95 	nvlist_get_ ## type (di[i], SNDST_DSPS_ ## item);		\
96 } while (0)
97 		NV(string, NAMEUNIT);
98 		NV(bool, FROM_USER);
99 		NV(string, DEVNODE);
100 		NV(string, DESC);
101 		NV(string, PROVIDER);
102 		NV(number, PCHAN);
103 		NV(number, RCHAN);
104 #undef NV
105 
106 		/* Cannot asign using the macro. */
107 		pchan = nvlist_get_number(di[i], SNDST_DSPS_PCHAN);
108 		rchan = nvlist_get_number(di[i], SNDST_DSPS_RCHAN);
109 
110 		if (pchan && !nvlist_exists(di[i], SNDST_DSPS_INFO_PLAY))
111 			atf_tc_fail("playback channel list empty");
112 		if (rchan && !nvlist_exists(di[i], SNDST_DSPS_INFO_REC))
113 			atf_tc_fail("recording channel list empty");
114 
115 #define NV(type, mode, item)	do {					\
116 	ATF_REQUIRE_MSG(nvlist_exists(nvlist_get_nvlist(di[i],		\
117 	    SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item),	\
118 	    "SNDST_DSPS_INFO_" #item " does not exist");		\
119 	nvlist_get_ ## type (nvlist_get_nvlist(di[i],			\
120 	    SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item);	\
121 } while (0)
122 		if (pchan) {
123 			NV(number, PLAY, MIN_RATE);
124 			NV(number, PLAY, MAX_RATE);
125 			NV(number, PLAY, FORMATS);
126 			NV(number, PLAY, MIN_CHN);
127 			NV(number, PLAY, MAX_CHN);
128 		}
129 		if (rchan) {
130 			NV(number, REC, MIN_RATE);
131 			NV(number, REC, MAX_RATE);
132 			NV(number, REC, FORMATS);
133 			NV(number, REC, MIN_CHN);
134 			NV(number, REC, MAX_CHN);
135 		}
136 #undef NV
137 
138 		/* XXX Do we need to skip the TC? userdevs won't have this list */
139 		if (!nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO))
140 			continue;
141 
142 #define NV(type, item)	do {						\
143 	ATF_REQUIRE_MSG(nvlist_exists(nvlist_get_nvlist(di[i],		\
144 	    SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item),	\
145 	    "SNDST_DSPS_SOUND4_" #item " does not exist");		\
146 	nvlist_get_ ## type (nvlist_get_nvlist(di[i],			\
147 	    SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item);	\
148 } while (0)
149 		NV(number, UNIT);
150 		NV(string, STATUS);
151 		NV(bool, BITPERFECT);
152 		NV(number, PVCHAN);
153 		NV(number, PVCHANRATE);
154 		NV(number, PVCHANFORMAT);
155 		NV(number, RVCHAN);
156 		NV(number, PVCHANRATE);
157 		NV(number, PVCHANFORMAT);
158 #undef NV
159 
160 		if (!nvlist_exists(nvlist_get_nvlist(di[i],
161 		    SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_CHAN_INFO))
162 			atf_tc_fail("channel info list empty");
163 
164 		cdi = nvlist_get_nvlist_array(
165 		    nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO),
166 		    SNDST_DSPS_SOUND4_CHAN_INFO, &nchans);
167 		for (j = 0; j < nchans; j++) {
168 #define NV(type, item)	do {							\
169 	ATF_REQUIRE_MSG(nvlist_exists(cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item),	\
170 	    "SNDST_DSPS_SOUND4_CHAN_" #item " does not exist");			\
171 	nvlist_get_ ## type (cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item);		\
172 } while (0)
173 			NV(string, NAME);
174 			NV(string, PARENTCHAN);
175 			NV(number, UNIT);
176 			NV(number, CAPS);
177 			NV(number, LATENCY);
178 			NV(number, RATE);
179 			NV(number, FORMAT);
180 			NV(number, PID);
181 			NV(string, COMM);
182 			NV(number, INTR);
183 			NV(number, XRUNS);
184 			NV(number, FEEDCNT);
185 			NV(number, LEFTVOL);
186 			NV(number, RIGHTVOL);
187 			NV(number, HWBUF_FORMAT);
188 			NV(number, HWBUF_SIZE);
189 			NV(number, HWBUF_BLKSZ);
190 			NV(number, HWBUF_BLKCNT);
191 			NV(number, HWBUF_FREE);
192 			NV(number, HWBUF_READY);
193 			NV(number, SWBUF_FORMAT);
194 			NV(number, SWBUF_SIZE);
195 			NV(number, SWBUF_BLKSZ);
196 			NV(number, SWBUF_BLKCNT);
197 			NV(number, SWBUF_FREE);
198 			NV(number, SWBUF_READY);
199 			NV(string, FEEDERCHAIN);
200 #undef NV
201 		}
202 	}
203 
204 	free(arg.buf);
205 	nvlist_destroy(nvl);
206 	close(fd);
207 }
208 
209 ATF_TP_ADD_TCS(tp)
210 {
211 	ATF_TP_ADD_TC(tp, sndstat_nv);
212 
213 	return (atf_no_error());
214 }
215