195eb4b87SChristos Margiolis /*-
295eb4b87SChristos Margiolis * SPDX-License-Identifier: BSD-2-Clause
395eb4b87SChristos Margiolis *
495eb4b87SChristos Margiolis * Copyright (c) 2024 The FreeBSD Foundation
595eb4b87SChristos Margiolis *
695eb4b87SChristos Margiolis * This software was developed by Christos Margiolis <christos@FreeBSD.org>
795eb4b87SChristos Margiolis * under sponsorship from the FreeBSD Foundation.
895eb4b87SChristos Margiolis *
995eb4b87SChristos Margiolis * Redistribution and use in source and binary forms, with or without
1095eb4b87SChristos Margiolis * modification, are permitted provided that the following conditions
1195eb4b87SChristos Margiolis * are met:
1295eb4b87SChristos Margiolis * 1. Redistributions of source code must retain the above copyright
1395eb4b87SChristos Margiolis * notice, this list of conditions and the following disclaimer.
1495eb4b87SChristos Margiolis * 2. Redistributions in binary form must reproduce the above copyright
1595eb4b87SChristos Margiolis * notice, this list of conditions and the following disclaimer in the
1695eb4b87SChristos Margiolis * documentation and/or other materials provided with the distribution.
1795eb4b87SChristos Margiolis *
1895eb4b87SChristos Margiolis * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1995eb4b87SChristos Margiolis * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2095eb4b87SChristos Margiolis * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2195eb4b87SChristos Margiolis * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2295eb4b87SChristos Margiolis * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2395eb4b87SChristos Margiolis * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2495eb4b87SChristos Margiolis * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2595eb4b87SChristos Margiolis * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2695eb4b87SChristos Margiolis * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2795eb4b87SChristos Margiolis * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2895eb4b87SChristos Margiolis * SUCH DAMAGE.
2995eb4b87SChristos Margiolis */
3095eb4b87SChristos Margiolis
3195eb4b87SChristos Margiolis #include <sys/param.h>
3295eb4b87SChristos Margiolis #include <sys/linker.h>
3395eb4b87SChristos Margiolis #include <sys/nv.h>
34*2668e76dSChristos Margiolis #include <sys/sndstat.h>
35*2668e76dSChristos Margiolis #include <sys/soundcard.h>
3695eb4b87SChristos Margiolis
3795eb4b87SChristos Margiolis #include <atf-c.h>
3895eb4b87SChristos Margiolis #include <errno.h>
3995eb4b87SChristos Margiolis #include <fcntl.h>
4095eb4b87SChristos Margiolis #include <stdlib.h>
4195eb4b87SChristos Margiolis #include <unistd.h>
4295eb4b87SChristos Margiolis
4395eb4b87SChristos Margiolis static void
load_dummy(void)4495eb4b87SChristos Margiolis load_dummy(void)
4595eb4b87SChristos Margiolis {
4695eb4b87SChristos Margiolis if (kldload("snd_dummy.ko") < 0 && errno != EEXIST)
4795eb4b87SChristos Margiolis atf_tc_skip("snd_dummy.ko not found");
4895eb4b87SChristos Margiolis }
4995eb4b87SChristos Margiolis
5095eb4b87SChristos Margiolis ATF_TC(sndstat_nv);
ATF_TC_HEAD(sndstat_nv,tc)5195eb4b87SChristos Margiolis ATF_TC_HEAD(sndstat_nv, tc)
5295eb4b87SChristos Margiolis {
5395eb4b87SChristos Margiolis atf_tc_set_md_var(tc, "descr", "/dev/sndstat nvlist test");
5495eb4b87SChristos Margiolis }
5595eb4b87SChristos Margiolis
ATF_TC_BODY(sndstat_nv,tc)5695eb4b87SChristos Margiolis ATF_TC_BODY(sndstat_nv, tc)
5795eb4b87SChristos Margiolis {
5895eb4b87SChristos Margiolis nvlist_t *nvl;
5995eb4b87SChristos Margiolis const nvlist_t * const *di;
6095eb4b87SChristos Margiolis const nvlist_t * const *cdi;
6195eb4b87SChristos Margiolis struct sndstioc_nv_arg arg;
6295eb4b87SChristos Margiolis size_t nitems, nchans, i, j;
63*2668e76dSChristos Margiolis int fd, rc, pchan, rchan;
6495eb4b87SChristos Margiolis
6595eb4b87SChristos Margiolis load_dummy();
6695eb4b87SChristos Margiolis
6795eb4b87SChristos Margiolis if ((fd = open("/dev/sndstat", O_RDONLY)) < 0)
6895eb4b87SChristos Margiolis atf_tc_skip("/dev/sndstat not found, load sound(4)");
6995eb4b87SChristos Margiolis
7095eb4b87SChristos Margiolis rc = ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL);
7195eb4b87SChristos Margiolis ATF_REQUIRE_EQ(rc, 0);
7295eb4b87SChristos Margiolis
7395eb4b87SChristos Margiolis arg.nbytes = 0;
7495eb4b87SChristos Margiolis arg.buf = NULL;
7595eb4b87SChristos Margiolis rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg);
7695eb4b87SChristos Margiolis ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#1) failed");
7795eb4b87SChristos Margiolis
7895eb4b87SChristos Margiolis arg.buf = malloc(arg.nbytes);
7995eb4b87SChristos Margiolis ATF_REQUIRE(arg.buf != NULL);
8095eb4b87SChristos Margiolis
8195eb4b87SChristos Margiolis rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg);
8295eb4b87SChristos Margiolis ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#2) failed");
8395eb4b87SChristos Margiolis
8495eb4b87SChristos Margiolis nvl = nvlist_unpack(arg.buf, arg.nbytes, 0);
8595eb4b87SChristos Margiolis ATF_REQUIRE(nvl != NULL);
8695eb4b87SChristos Margiolis
8795eb4b87SChristos Margiolis if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS))
8895eb4b87SChristos Margiolis atf_tc_skip("no soundcards attached");
8995eb4b87SChristos Margiolis
9095eb4b87SChristos Margiolis di = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems);
9195eb4b87SChristos Margiolis for (i = 0; i < nitems; i++) {
9295eb4b87SChristos Margiolis #define NV(type, item) do { \
9395eb4b87SChristos Margiolis ATF_REQUIRE_MSG(nvlist_exists(di[i], SNDST_DSPS_ ## item), \
9495eb4b87SChristos Margiolis "SNDST_DSPS_" #item " does not exist"); \
9595eb4b87SChristos Margiolis nvlist_get_ ## type (di[i], SNDST_DSPS_ ## item); \
9695eb4b87SChristos Margiolis } while (0)
9795eb4b87SChristos Margiolis NV(string, NAMEUNIT);
9895eb4b87SChristos Margiolis NV(bool, FROM_USER);
9995eb4b87SChristos Margiolis NV(string, DEVNODE);
10095eb4b87SChristos Margiolis NV(string, DESC);
10195eb4b87SChristos Margiolis NV(string, PROVIDER);
10295eb4b87SChristos Margiolis NV(number, PCHAN);
10395eb4b87SChristos Margiolis NV(number, RCHAN);
10495eb4b87SChristos Margiolis #undef NV
10595eb4b87SChristos Margiolis
10695eb4b87SChristos Margiolis /* Cannot asign using the macro. */
10795eb4b87SChristos Margiolis pchan = nvlist_get_number(di[i], SNDST_DSPS_PCHAN);
10895eb4b87SChristos Margiolis rchan = nvlist_get_number(di[i], SNDST_DSPS_RCHAN);
10995eb4b87SChristos Margiolis
11095eb4b87SChristos Margiolis if (pchan && !nvlist_exists(di[i], SNDST_DSPS_INFO_PLAY))
11195eb4b87SChristos Margiolis atf_tc_fail("playback channel list empty");
11295eb4b87SChristos Margiolis if (rchan && !nvlist_exists(di[i], SNDST_DSPS_INFO_REC))
11395eb4b87SChristos Margiolis atf_tc_fail("recording channel list empty");
11495eb4b87SChristos Margiolis
11595eb4b87SChristos Margiolis #define NV(type, mode, item) do { \
11695eb4b87SChristos Margiolis ATF_REQUIRE_MSG(nvlist_exists(nvlist_get_nvlist(di[i], \
11795eb4b87SChristos Margiolis SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item), \
11895eb4b87SChristos Margiolis "SNDST_DSPS_INFO_" #item " does not exist"); \
11995eb4b87SChristos Margiolis nvlist_get_ ## type (nvlist_get_nvlist(di[i], \
12095eb4b87SChristos Margiolis SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item); \
12195eb4b87SChristos Margiolis } while (0)
12295eb4b87SChristos Margiolis if (pchan) {
12395eb4b87SChristos Margiolis NV(number, PLAY, MIN_RATE);
12495eb4b87SChristos Margiolis NV(number, PLAY, MAX_RATE);
12595eb4b87SChristos Margiolis NV(number, PLAY, FORMATS);
12695eb4b87SChristos Margiolis NV(number, PLAY, MIN_CHN);
12795eb4b87SChristos Margiolis NV(number, PLAY, MAX_CHN);
12895eb4b87SChristos Margiolis }
12995eb4b87SChristos Margiolis if (rchan) {
13095eb4b87SChristos Margiolis NV(number, REC, MIN_RATE);
13195eb4b87SChristos Margiolis NV(number, REC, MAX_RATE);
13295eb4b87SChristos Margiolis NV(number, REC, FORMATS);
13395eb4b87SChristos Margiolis NV(number, REC, MIN_CHN);
13495eb4b87SChristos Margiolis NV(number, REC, MAX_CHN);
13595eb4b87SChristos Margiolis }
13695eb4b87SChristos Margiolis #undef NV
13795eb4b87SChristos Margiolis
13895eb4b87SChristos Margiolis if (!nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO))
13995eb4b87SChristos Margiolis continue;
14095eb4b87SChristos Margiolis
14195eb4b87SChristos Margiolis #define NV(type, item) do { \
14295eb4b87SChristos Margiolis ATF_REQUIRE_MSG(nvlist_exists(nvlist_get_nvlist(di[i], \
14395eb4b87SChristos Margiolis SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item), \
14495eb4b87SChristos Margiolis "SNDST_DSPS_SOUND4_" #item " does not exist"); \
14595eb4b87SChristos Margiolis nvlist_get_ ## type (nvlist_get_nvlist(di[i], \
14695eb4b87SChristos Margiolis SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item); \
14795eb4b87SChristos Margiolis } while (0)
14895eb4b87SChristos Margiolis NV(number, UNIT);
14995eb4b87SChristos Margiolis NV(string, STATUS);
15095eb4b87SChristos Margiolis NV(bool, BITPERFECT);
15195eb4b87SChristos Margiolis NV(number, PVCHAN);
15295eb4b87SChristos Margiolis NV(number, PVCHANRATE);
15395eb4b87SChristos Margiolis NV(number, PVCHANFORMAT);
15495eb4b87SChristos Margiolis NV(number, RVCHAN);
15595eb4b87SChristos Margiolis NV(number, PVCHANRATE);
15695eb4b87SChristos Margiolis NV(number, PVCHANFORMAT);
15795eb4b87SChristos Margiolis #undef NV
15895eb4b87SChristos Margiolis
15995eb4b87SChristos Margiolis if (!nvlist_exists(nvlist_get_nvlist(di[i],
16095eb4b87SChristos Margiolis SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_CHAN_INFO))
16195eb4b87SChristos Margiolis atf_tc_fail("channel info list empty");
16295eb4b87SChristos Margiolis
16395eb4b87SChristos Margiolis cdi = nvlist_get_nvlist_array(
16495eb4b87SChristos Margiolis nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO),
16595eb4b87SChristos Margiolis SNDST_DSPS_SOUND4_CHAN_INFO, &nchans);
16695eb4b87SChristos Margiolis for (j = 0; j < nchans; j++) {
16795eb4b87SChristos Margiolis #define NV(type, item) do { \
16895eb4b87SChristos Margiolis ATF_REQUIRE_MSG(nvlist_exists(cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item), \
16995eb4b87SChristos Margiolis "SNDST_DSPS_SOUND4_CHAN_" #item " does not exist"); \
17095eb4b87SChristos Margiolis nvlist_get_ ## type (cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item); \
17195eb4b87SChristos Margiolis } while (0)
17295eb4b87SChristos Margiolis NV(string, NAME);
17395eb4b87SChristos Margiolis NV(string, PARENTCHAN);
17495eb4b87SChristos Margiolis NV(number, UNIT);
17595eb4b87SChristos Margiolis NV(number, CAPS);
17695eb4b87SChristos Margiolis NV(number, LATENCY);
17795eb4b87SChristos Margiolis NV(number, RATE);
17895eb4b87SChristos Margiolis NV(number, FORMAT);
17995eb4b87SChristos Margiolis NV(number, PID);
18095eb4b87SChristos Margiolis NV(string, COMM);
18195eb4b87SChristos Margiolis NV(number, INTR);
18295eb4b87SChristos Margiolis NV(number, XRUNS);
18395eb4b87SChristos Margiolis NV(number, FEEDCNT);
18495eb4b87SChristos Margiolis NV(number, LEFTVOL);
18595eb4b87SChristos Margiolis NV(number, RIGHTVOL);
18695eb4b87SChristos Margiolis NV(number, HWBUF_FORMAT);
18795eb4b87SChristos Margiolis NV(number, HWBUF_SIZE);
18895eb4b87SChristos Margiolis NV(number, HWBUF_BLKSZ);
18995eb4b87SChristos Margiolis NV(number, HWBUF_BLKCNT);
19095eb4b87SChristos Margiolis NV(number, HWBUF_FREE);
19195eb4b87SChristos Margiolis NV(number, HWBUF_READY);
19295eb4b87SChristos Margiolis NV(number, SWBUF_FORMAT);
19395eb4b87SChristos Margiolis NV(number, SWBUF_SIZE);
19495eb4b87SChristos Margiolis NV(number, SWBUF_BLKSZ);
19595eb4b87SChristos Margiolis NV(number, SWBUF_BLKCNT);
19695eb4b87SChristos Margiolis NV(number, SWBUF_FREE);
19795eb4b87SChristos Margiolis NV(number, SWBUF_READY);
19895eb4b87SChristos Margiolis NV(string, FEEDERCHAIN);
19995eb4b87SChristos Margiolis #undef NV
20095eb4b87SChristos Margiolis }
20195eb4b87SChristos Margiolis }
20295eb4b87SChristos Margiolis
20395eb4b87SChristos Margiolis free(arg.buf);
20495eb4b87SChristos Margiolis nvlist_destroy(nvl);
20595eb4b87SChristos Margiolis close(fd);
20695eb4b87SChristos Margiolis }
20795eb4b87SChristos Margiolis
208*2668e76dSChristos Margiolis #define UDEV_PROVIDER "sndstat_udev"
209*2668e76dSChristos Margiolis #define UDEV_NAMEUNIT "sndstat_udev"
210*2668e76dSChristos Margiolis #define UDEV_DEVNODE "sndstat_udev"
211*2668e76dSChristos Margiolis #define UDEV_DESC "Test Device"
212*2668e76dSChristos Margiolis #define UDEV_PCHAN 1
213*2668e76dSChristos Margiolis #define UDEV_RCHAN 1
214*2668e76dSChristos Margiolis #define UDEV_MIN_RATE 8000
215*2668e76dSChristos Margiolis #define UDEV_MAX_RATE 96000
216*2668e76dSChristos Margiolis #define UDEV_FORMATS (AFMT_S16_NE | AFMT_S24_NE | AFMT_S32_NE)
217*2668e76dSChristos Margiolis #define UDEV_MIN_CHN 1
218*2668e76dSChristos Margiolis #define UDEV_MAX_CHN 2
219*2668e76dSChristos Margiolis
220*2668e76dSChristos Margiolis ATF_TC(sndstat_udev);
ATF_TC_HEAD(sndstat_udev,tc)221*2668e76dSChristos Margiolis ATF_TC_HEAD(sndstat_udev, tc)
222*2668e76dSChristos Margiolis {
223*2668e76dSChristos Margiolis atf_tc_set_md_var(tc, "descr", "/dev/sndstat userdev interface test");
224*2668e76dSChristos Margiolis }
225*2668e76dSChristos Margiolis
ATF_TC_BODY(sndstat_udev,tc)226*2668e76dSChristos Margiolis ATF_TC_BODY(sndstat_udev, tc)
227*2668e76dSChristos Margiolis {
228*2668e76dSChristos Margiolis nvlist_t *nvl, *di, *dichild;
229*2668e76dSChristos Margiolis const nvlist_t * const *rdi;
230*2668e76dSChristos Margiolis struct sndstioc_nv_arg arg;
231*2668e76dSChristos Margiolis const char *str;
232*2668e76dSChristos Margiolis size_t nitems, i;
233*2668e76dSChristos Margiolis int fd, rc, pchan, rchan, n;
234*2668e76dSChristos Margiolis
235*2668e76dSChristos Margiolis load_dummy();
236*2668e76dSChristos Margiolis
237*2668e76dSChristos Margiolis if ((fd = open("/dev/sndstat", O_RDWR)) < 0)
238*2668e76dSChristos Margiolis atf_tc_skip("/dev/sndstat not found, load sound(4)");
239*2668e76dSChristos Margiolis
240*2668e76dSChristos Margiolis nvl = nvlist_create(0);
241*2668e76dSChristos Margiolis ATF_REQUIRE(nvl != NULL);
242*2668e76dSChristos Margiolis
243*2668e76dSChristos Margiolis di = nvlist_create(0);
244*2668e76dSChristos Margiolis ATF_REQUIRE(di != NULL);
245*2668e76dSChristos Margiolis
246*2668e76dSChristos Margiolis dichild = nvlist_create(0);
247*2668e76dSChristos Margiolis ATF_REQUIRE(dichild != NULL);
248*2668e76dSChristos Margiolis
249*2668e76dSChristos Margiolis nvlist_add_string(di, SNDST_DSPS_PROVIDER, UDEV_PROVIDER);
250*2668e76dSChristos Margiolis nvlist_add_string(di, SNDST_DSPS_NAMEUNIT, UDEV_NAMEUNIT);
251*2668e76dSChristos Margiolis nvlist_add_string(di, SNDST_DSPS_DESC, UDEV_DESC);
252*2668e76dSChristos Margiolis nvlist_add_string(di, SNDST_DSPS_DEVNODE, UDEV_DEVNODE);
253*2668e76dSChristos Margiolis nvlist_add_number(di, SNDST_DSPS_PCHAN, UDEV_PCHAN);
254*2668e76dSChristos Margiolis nvlist_add_number(di, SNDST_DSPS_RCHAN, UDEV_RCHAN);
255*2668e76dSChristos Margiolis
256*2668e76dSChristos Margiolis nvlist_add_number(dichild, SNDST_DSPS_INFO_MIN_RATE, UDEV_MIN_RATE);
257*2668e76dSChristos Margiolis nvlist_add_number(dichild, SNDST_DSPS_INFO_MAX_RATE, UDEV_MAX_RATE);
258*2668e76dSChristos Margiolis nvlist_add_number(dichild, SNDST_DSPS_INFO_FORMATS, UDEV_FORMATS);
259*2668e76dSChristos Margiolis nvlist_add_number(dichild, SNDST_DSPS_INFO_MIN_CHN, UDEV_MIN_CHN);
260*2668e76dSChristos Margiolis nvlist_add_number(dichild, SNDST_DSPS_INFO_MAX_CHN, UDEV_MAX_CHN);
261*2668e76dSChristos Margiolis
262*2668e76dSChristos Margiolis nvlist_add_nvlist(di, SNDST_DSPS_INFO_PLAY, dichild);
263*2668e76dSChristos Margiolis nvlist_add_nvlist(di, SNDST_DSPS_INFO_REC, dichild);
264*2668e76dSChristos Margiolis
265*2668e76dSChristos Margiolis nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
266*2668e76dSChristos Margiolis ATF_REQUIRE_EQ(nvlist_error(nvl), 0);
267*2668e76dSChristos Margiolis
268*2668e76dSChristos Margiolis arg.buf = nvlist_pack(nvl, &arg.nbytes);
269*2668e76dSChristos Margiolis ATF_REQUIRE_MSG(arg.buf != NULL, "failed to pack nvlist");
270*2668e76dSChristos Margiolis
271*2668e76dSChristos Margiolis rc = ioctl(fd, SNDSTIOC_ADD_USER_DEVS, &arg);
272*2668e76dSChristos Margiolis free(arg.buf);
273*2668e76dSChristos Margiolis ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_ADD_USER_DEVS) failed");
274*2668e76dSChristos Margiolis
275*2668e76dSChristos Margiolis nvlist_destroy(di);
276*2668e76dSChristos Margiolis nvlist_destroy(dichild);
277*2668e76dSChristos Margiolis nvlist_destroy(nvl);
278*2668e76dSChristos Margiolis
279*2668e76dSChristos Margiolis /* Read back registered values. */
280*2668e76dSChristos Margiolis rc = ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL);
281*2668e76dSChristos Margiolis ATF_REQUIRE_EQ(rc, 0);
282*2668e76dSChristos Margiolis
283*2668e76dSChristos Margiolis arg.nbytes = 0;
284*2668e76dSChristos Margiolis arg.buf = NULL;
285*2668e76dSChristos Margiolis rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg);
286*2668e76dSChristos Margiolis ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#1) failed");
287*2668e76dSChristos Margiolis
288*2668e76dSChristos Margiolis arg.buf = malloc(arg.nbytes);
289*2668e76dSChristos Margiolis ATF_REQUIRE(arg.buf != NULL);
290*2668e76dSChristos Margiolis
291*2668e76dSChristos Margiolis rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg);
292*2668e76dSChristos Margiolis ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#2) failed");
293*2668e76dSChristos Margiolis
294*2668e76dSChristos Margiolis nvl = nvlist_unpack(arg.buf, arg.nbytes, 0);
295*2668e76dSChristos Margiolis ATF_REQUIRE(nvl != NULL);
296*2668e76dSChristos Margiolis
297*2668e76dSChristos Margiolis if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS))
298*2668e76dSChristos Margiolis atf_tc_skip("no soundcards attached");
299*2668e76dSChristos Margiolis
300*2668e76dSChristos Margiolis rdi = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems);
301*2668e76dSChristos Margiolis for (i = 0; i < nitems; i++) {
302*2668e76dSChristos Margiolis #define NV(type, item, var) do { \
303*2668e76dSChristos Margiolis ATF_REQUIRE_MSG(nvlist_exists(rdi[i], SNDST_DSPS_ ## item), \
304*2668e76dSChristos Margiolis "SNDST_DSPS_" #item " does not exist"); \
305*2668e76dSChristos Margiolis var = nvlist_get_ ## type (rdi[i], SNDST_DSPS_ ## item); \
306*2668e76dSChristos Margiolis } while (0)
307*2668e76dSChristos Margiolis /* Search for our device. */
308*2668e76dSChristos Margiolis NV(string, NAMEUNIT, str);
309*2668e76dSChristos Margiolis if (strcmp(str, UDEV_NAMEUNIT) == 0)
310*2668e76dSChristos Margiolis break;
311*2668e76dSChristos Margiolis }
312*2668e76dSChristos Margiolis if (i == nitems)
313*2668e76dSChristos Margiolis atf_tc_fail("userland device %s not found", UDEV_NAMEUNIT);
314*2668e76dSChristos Margiolis
315*2668e76dSChristos Margiolis NV(string, NAMEUNIT, str);
316*2668e76dSChristos Margiolis ATF_CHECK(strcmp(str, UDEV_NAMEUNIT) == 0);
317*2668e76dSChristos Margiolis
318*2668e76dSChristos Margiolis NV(bool, FROM_USER, n);
319*2668e76dSChristos Margiolis ATF_CHECK(n);
320*2668e76dSChristos Margiolis
321*2668e76dSChristos Margiolis NV(string, DEVNODE, str);
322*2668e76dSChristos Margiolis ATF_CHECK(strcmp(str, UDEV_DEVNODE) == 0);
323*2668e76dSChristos Margiolis
324*2668e76dSChristos Margiolis NV(string, DESC, str);
325*2668e76dSChristos Margiolis ATF_CHECK(strcmp(str, UDEV_DESC) == 0);
326*2668e76dSChristos Margiolis
327*2668e76dSChristos Margiolis NV(string, PROVIDER, str);
328*2668e76dSChristos Margiolis ATF_CHECK(strcmp(str, UDEV_PROVIDER) == 0);
329*2668e76dSChristos Margiolis
330*2668e76dSChristos Margiolis NV(number, PCHAN, pchan);
331*2668e76dSChristos Margiolis ATF_CHECK(pchan == UDEV_PCHAN);
332*2668e76dSChristos Margiolis if (pchan && !nvlist_exists(rdi[i], SNDST_DSPS_INFO_PLAY))
333*2668e76dSChristos Margiolis atf_tc_fail("playback channel list empty");
334*2668e76dSChristos Margiolis
335*2668e76dSChristos Margiolis NV(number, RCHAN, rchan);
336*2668e76dSChristos Margiolis ATF_CHECK(rchan == UDEV_RCHAN);
337*2668e76dSChristos Margiolis if (rchan && !nvlist_exists(rdi[i], SNDST_DSPS_INFO_REC))
338*2668e76dSChristos Margiolis atf_tc_fail("recording channel list empty");
339*2668e76dSChristos Margiolis #undef NV
340*2668e76dSChristos Margiolis
341*2668e76dSChristos Margiolis #define NV(type, mode, item, var) do { \
342*2668e76dSChristos Margiolis ATF_REQUIRE_MSG(nvlist_exists(nvlist_get_nvlist(rdi[i], \
343*2668e76dSChristos Margiolis SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item), \
344*2668e76dSChristos Margiolis "SNDST_DSPS_INFO_" #item " does not exist"); \
345*2668e76dSChristos Margiolis var = nvlist_get_ ## type (nvlist_get_nvlist(rdi[i], \
346*2668e76dSChristos Margiolis SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item); \
347*2668e76dSChristos Margiolis } while (0)
348*2668e76dSChristos Margiolis if (pchan) {
349*2668e76dSChristos Margiolis NV(number, PLAY, MIN_RATE, n);
350*2668e76dSChristos Margiolis ATF_CHECK(n == UDEV_MIN_RATE);
351*2668e76dSChristos Margiolis
352*2668e76dSChristos Margiolis NV(number, PLAY, MAX_RATE, n);
353*2668e76dSChristos Margiolis ATF_CHECK(n == UDEV_MAX_RATE);
354*2668e76dSChristos Margiolis
355*2668e76dSChristos Margiolis NV(number, PLAY, FORMATS, n);
356*2668e76dSChristos Margiolis ATF_CHECK(n == UDEV_FORMATS);
357*2668e76dSChristos Margiolis
358*2668e76dSChristos Margiolis NV(number, PLAY, MIN_CHN, n);
359*2668e76dSChristos Margiolis ATF_CHECK(n == UDEV_MIN_CHN);
360*2668e76dSChristos Margiolis
361*2668e76dSChristos Margiolis NV(number, PLAY, MAX_CHN, n);
362*2668e76dSChristos Margiolis ATF_CHECK(n == UDEV_MAX_CHN);
363*2668e76dSChristos Margiolis }
364*2668e76dSChristos Margiolis if (rchan) {
365*2668e76dSChristos Margiolis NV(number, REC, MIN_RATE, n);
366*2668e76dSChristos Margiolis ATF_CHECK(n == UDEV_MIN_RATE);
367*2668e76dSChristos Margiolis
368*2668e76dSChristos Margiolis NV(number, REC, MAX_RATE, n);
369*2668e76dSChristos Margiolis ATF_CHECK(n == UDEV_MAX_RATE);
370*2668e76dSChristos Margiolis
371*2668e76dSChristos Margiolis NV(number, REC, FORMATS, n);
372*2668e76dSChristos Margiolis ATF_CHECK(n == UDEV_FORMATS);
373*2668e76dSChristos Margiolis
374*2668e76dSChristos Margiolis NV(number, REC, MIN_CHN, n);
375*2668e76dSChristos Margiolis ATF_CHECK(n == UDEV_MIN_CHN);
376*2668e76dSChristos Margiolis
377*2668e76dSChristos Margiolis NV(number, REC, MAX_CHN, n);
378*2668e76dSChristos Margiolis ATF_CHECK(n == UDEV_MAX_CHN);
379*2668e76dSChristos Margiolis }
380*2668e76dSChristos Margiolis #undef NV
381*2668e76dSChristos Margiolis
382*2668e76dSChristos Margiolis free(arg.buf);
383*2668e76dSChristos Margiolis nvlist_destroy(nvl);
384*2668e76dSChristos Margiolis close(fd);
385*2668e76dSChristos Margiolis }
386*2668e76dSChristos Margiolis
ATF_TP_ADD_TCS(tp)38795eb4b87SChristos Margiolis ATF_TP_ADD_TCS(tp)
38895eb4b87SChristos Margiolis {
38995eb4b87SChristos Margiolis ATF_TP_ADD_TC(tp, sndstat_nv);
390*2668e76dSChristos Margiolis ATF_TP_ADD_TC(tp, sndstat_udev);
39195eb4b87SChristos Margiolis
39295eb4b87SChristos Margiolis return (atf_no_error());
39395eb4b87SChristos Margiolis }
394