diff --git a/tests/sys/Makefile b/tests/sys/Makefile index 20ea4f181c7c..a2a133e19803 100644 --- a/tests/sys/Makefile +++ b/tests/sys/Makefile @@ -1,56 +1,57 @@ .include TESTSDIR= ${TESTSBASE}/sys TESTS_SUBDIRS+= acl TESTS_SUBDIRS+= aio TESTS_SUBDIRS+= ${_audit} TESTS_SUBDIRS+= auditpipe TESTS_SUBDIRS+= capsicum TESTS_SUBDIRS+= ${_cddl} # XXX: Currently broken in CI #TESTS_SUBDIRS+= compat32 TESTS_SUBDIRS+= devrandom TESTS_SUBDIRS+= fifo TESTS_SUBDIRS+= file TESTS_SUBDIRS+= fs TESTS_SUBDIRS+= geom TESTS_SUBDIRS+= kern TESTS_SUBDIRS+= kqueue TESTS_SUBDIRS+= mac TESTS_SUBDIRS+= mqueue TESTS_SUBDIRS+= net TESTS_SUBDIRS+= ${_netgraph} TESTS_SUBDIRS+= netinet TESTS_SUBDIRS+= netinet6 TESTS_SUBDIRS+= netipsec TESTS_SUBDIRS+= netlink TESTS_SUBDIRS+= netmap TESTS_SUBDIRS+= netpfil TESTS_SUBDIRS+= opencrypto TESTS_SUBDIRS+= posixshm TESTS_SUBDIRS+= ses +TESTS_SUBDIRS+= sound TESTS_SUBDIRS+= sys TESTS_SUBDIRS+= vfs TESTS_SUBDIRS+= vm TESTS_SUBDIRS+= vmm .if ${MK_AUDIT} != "no" _audit= audit .endif .if ${MK_CDDL} != "no" _cddl= cddl .endif .if ${MK_NETGRAPH} != "no" _netgraph= netgraph .endif # Items not integrated into kyua runs by default SUBDIR+= pjdfstest SUBDIR+= common .include diff --git a/tests/sys/sound/Makefile b/tests/sys/sound/Makefile new file mode 100644 index 000000000000..fb731fb8ab61 --- /dev/null +++ b/tests/sys/sound/Makefile @@ -0,0 +1,9 @@ +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/sys/sound + +ATF_TESTS_C+= sndstat + +LDFLAGS+= -lnv + +.include diff --git a/tests/sys/sound/sndstat.c b/tests/sys/sound/sndstat.c new file mode 100644 index 000000000000..7c030dfa8b58 --- /dev/null +++ b/tests/sys/sound/sndstat.c @@ -0,0 +1,214 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 The FreeBSD Foundation + * + * This software was developed by Christos Margiolis + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static void +load_dummy(void) +{ + if (kldload("snd_dummy.ko") < 0 && errno != EEXIST) + atf_tc_skip("snd_dummy.ko not found"); +} + +ATF_TC(sndstat_nv); +ATF_TC_HEAD(sndstat_nv, tc) +{ + atf_tc_set_md_var(tc, "descr", "/dev/sndstat nvlist test"); +} + +ATF_TC_BODY(sndstat_nv, tc) +{ + nvlist_t *nvl; + const nvlist_t * const *di; + const nvlist_t * const *cdi; + struct sndstioc_nv_arg arg; + size_t nitems, nchans, i, j; + int fd, rc; + int pchan, rchan; + + load_dummy(); + + if ((fd = open("/dev/sndstat", O_RDONLY)) < 0) + atf_tc_skip("/dev/sndstat not found, load sound(4)"); + + rc = ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL); + ATF_REQUIRE_EQ(rc, 0); + + arg.nbytes = 0; + arg.buf = NULL; + rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg); + ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#1) failed"); + + arg.buf = malloc(arg.nbytes); + ATF_REQUIRE(arg.buf != NULL); + + rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg); + ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#2) failed"); + + nvl = nvlist_unpack(arg.buf, arg.nbytes, 0); + ATF_REQUIRE(nvl != NULL); + + if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS)) + atf_tc_skip("no soundcards attached"); + + di = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems); + for (i = 0; i < nitems; i++) { +#define NV(type, item) do { \ + ATF_REQUIRE_MSG(nvlist_exists(di[i], SNDST_DSPS_ ## item), \ + "SNDST_DSPS_" #item " does not exist"); \ + nvlist_get_ ## type (di[i], SNDST_DSPS_ ## item); \ +} while (0) + NV(string, NAMEUNIT); + NV(bool, FROM_USER); + NV(string, DEVNODE); + NV(string, DESC); + NV(string, PROVIDER); + NV(number, PCHAN); + NV(number, RCHAN); +#undef NV + + /* Cannot asign using the macro. */ + pchan = nvlist_get_number(di[i], SNDST_DSPS_PCHAN); + rchan = nvlist_get_number(di[i], SNDST_DSPS_RCHAN); + + if (pchan && !nvlist_exists(di[i], SNDST_DSPS_INFO_PLAY)) + atf_tc_fail("playback channel list empty"); + if (rchan && !nvlist_exists(di[i], SNDST_DSPS_INFO_REC)) + atf_tc_fail("recording channel list empty"); + +#define NV(type, mode, item) do { \ + ATF_REQUIRE_MSG(nvlist_exists(nvlist_get_nvlist(di[i], \ + SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item), \ + "SNDST_DSPS_INFO_" #item " does not exist"); \ + nvlist_get_ ## type (nvlist_get_nvlist(di[i], \ + SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item); \ +} while (0) + if (pchan) { + NV(number, PLAY, MIN_RATE); + NV(number, PLAY, MAX_RATE); + NV(number, PLAY, FORMATS); + NV(number, PLAY, MIN_CHN); + NV(number, PLAY, MAX_CHN); + } + if (rchan) { + NV(number, REC, MIN_RATE); + NV(number, REC, MAX_RATE); + NV(number, REC, FORMATS); + NV(number, REC, MIN_CHN); + NV(number, REC, MAX_CHN); + } +#undef NV + + /* XXX Do we need to skip the TC? userdevs won't have this list */ + if (!nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO)) + continue; + +#define NV(type, item) do { \ + ATF_REQUIRE_MSG(nvlist_exists(nvlist_get_nvlist(di[i], \ + SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item), \ + "SNDST_DSPS_SOUND4_" #item " does not exist"); \ + nvlist_get_ ## type (nvlist_get_nvlist(di[i], \ + SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item); \ +} while (0) + NV(number, UNIT); + NV(string, STATUS); + NV(bool, BITPERFECT); + NV(number, PVCHAN); + NV(number, PVCHANRATE); + NV(number, PVCHANFORMAT); + NV(number, RVCHAN); + NV(number, PVCHANRATE); + NV(number, PVCHANFORMAT); +#undef NV + + if (!nvlist_exists(nvlist_get_nvlist(di[i], + SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_CHAN_INFO)) + atf_tc_fail("channel info list empty"); + + cdi = nvlist_get_nvlist_array( + nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO), + SNDST_DSPS_SOUND4_CHAN_INFO, &nchans); + for (j = 0; j < nchans; j++) { +#define NV(type, item) do { \ + ATF_REQUIRE_MSG(nvlist_exists(cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item), \ + "SNDST_DSPS_SOUND4_CHAN_" #item " does not exist"); \ + nvlist_get_ ## type (cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item); \ +} while (0) + NV(string, NAME); + NV(string, PARENTCHAN); + NV(number, UNIT); + NV(number, CAPS); + NV(number, LATENCY); + NV(number, RATE); + NV(number, FORMAT); + NV(number, PID); + NV(string, COMM); + NV(number, INTR); + NV(number, XRUNS); + NV(number, FEEDCNT); + NV(number, LEFTVOL); + NV(number, RIGHTVOL); + NV(number, HWBUF_FORMAT); + NV(number, HWBUF_SIZE); + NV(number, HWBUF_BLKSZ); + NV(number, HWBUF_BLKCNT); + NV(number, HWBUF_FREE); + NV(number, HWBUF_READY); + NV(number, SWBUF_FORMAT); + NV(number, SWBUF_SIZE); + NV(number, SWBUF_BLKSZ); + NV(number, SWBUF_BLKCNT); + NV(number, SWBUF_FREE); + NV(number, SWBUF_READY); + NV(string, FEEDERCHAIN); +#undef NV + } + } + + free(arg.buf); + nvlist_destroy(nvl); + close(fd); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, sndstat_nv); + + return (atf_no_error()); +}