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 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); 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 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); 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 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 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