1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2025 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/event.h>
32 #include <sys/soundcard.h>
33
34 #include <atf-c.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <inttypes.h>
38 #include <poll.h>
39 #include <unistd.h>
40
41 #define FMT_ERR(s) s ": %s", strerror(errno)
42
43 static int
oss_init(void)44 oss_init(void)
45 {
46 int fd, tmp, rc;
47
48 fd = open("/dev/dsp.dummy", O_RDWR);
49 ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open"));
50
51 tmp = 2;
52 rc = ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp);
53 ATF_REQUIRE_EQ_MSG(rc, 0, FMT_ERR("ioctl"));
54
55 tmp = AFMT_S16_LE;
56 rc = ioctl(fd, SNDCTL_DSP_SETFMT, &tmp);
57 ATF_REQUIRE_EQ_MSG(rc, 0, FMT_ERR("ioctl"));
58
59 tmp = 48000;
60 rc = ioctl(fd, SNDCTL_DSP_SPEED, &tmp);
61 ATF_REQUIRE_EQ_MSG(rc, 0, FMT_ERR("ioctl"));
62
63 /*
64 * See http://manuals.opensound.com/developer/SNDCTL_DSP_SETTRIGGER.html
65 */
66 tmp = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
67 rc = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);
68 ATF_REQUIRE_EQ_MSG(rc, 0, FMT_ERR("ioctl"));
69
70 return (fd);
71 }
72
73 ATF_TC(poll_kqueue);
ATF_TC_HEAD(poll_kqueue,tc)74 ATF_TC_HEAD(poll_kqueue, tc)
75 {
76 atf_tc_set_md_var(tc, "descr", "kqueue(2) test");
77 atf_tc_set_md_var(tc, "require.kmods", "snd_dummy");
78 }
79
ATF_TC_BODY(poll_kqueue,tc)80 ATF_TC_BODY(poll_kqueue, tc)
81 {
82 struct kevent ev;
83 int16_t buf[32];
84 int fd, kq;
85
86 fd = oss_init();
87
88 kq = kqueue();
89 ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
90 EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
91 ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
92 FMT_ERR("kevent"));
93
94 ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1,
95 FMT_ERR("kevent"));
96 ATF_REQUIRE_MSG((ev.flags & EV_ERROR) == 0, "EV_ERROR is set");
97 ATF_REQUIRE_MSG(ev.data != 0, "data is %" PRId64, ev.data);
98 ATF_REQUIRE_MSG(read(fd, buf, sizeof(buf)) > 0, FMT_ERR("read"));
99
100 EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
101 close(kq);
102
103 kq = kqueue();
104 ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
105 EV_SET(&ev, fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, 0);
106 ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
107 FMT_ERR("kevent"));
108
109 ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1,
110 FMT_ERR("kevent"));
111 ATF_REQUIRE_MSG((ev.flags & EV_ERROR) == 0, "EV_ERROR is set");
112 ATF_REQUIRE_MSG(ev.data != 0, "data is %" PRId64, ev.data);
113 ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) > 0, FMT_ERR("write"));
114
115 EV_SET(&ev, fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
116 close(kq);
117
118 close(fd);
119 }
120
121 ATF_TC(poll_poll);
ATF_TC_HEAD(poll_poll,tc)122 ATF_TC_HEAD(poll_poll, tc)
123 {
124 atf_tc_set_md_var(tc, "descr", "poll(2) test");
125 atf_tc_set_md_var(tc, "require.kmods", "snd_dummy");
126 }
127
ATF_TC_BODY(poll_poll,tc)128 ATF_TC_BODY(poll_poll, tc)
129 {
130 struct pollfd pfd[2];
131 int16_t buf[32];
132 int fd;
133 bool rd = false;
134 bool wr = false;
135
136 fd = oss_init();
137
138 while (!rd || !wr) {
139 pfd[0].fd = fd;
140 pfd[0].events = POLLIN;
141 pfd[1].fd = fd;
142 pfd[1].events = POLLOUT;
143 ATF_REQUIRE_MSG(poll(pfd, sizeof(pfd) / sizeof(struct pollfd),
144 -1) > 0, FMT_ERR("poll"));
145
146 if (pfd[0].revents) {
147 ATF_REQUIRE_MSG(read(fd, buf, sizeof(buf)) > 0,
148 FMT_ERR("read"));
149 rd = true;
150 }
151 if (pfd[1].revents) {
152 ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) > 0,
153 FMT_ERR("write"));
154 wr = true;
155 }
156 }
157 close(fd);
158 }
159
160 ATF_TC(poll_select);
ATF_TC_HEAD(poll_select,tc)161 ATF_TC_HEAD(poll_select, tc)
162 {
163 atf_tc_set_md_var(tc, "descr", "select(2) test");
164 atf_tc_set_md_var(tc, "require.kmods", "snd_dummy");
165 }
166
ATF_TC_BODY(poll_select,tc)167 ATF_TC_BODY(poll_select, tc)
168 {
169 fd_set fds[2];
170 int16_t buf[32];
171 int fd;
172 bool rd = false;
173 bool wr = false;
174
175 fd = oss_init();
176
177 while (!rd || !wr) {
178 FD_ZERO(&fds[0]);
179 FD_ZERO(&fds[1]);
180 FD_SET(fd, &fds[0]);
181 FD_SET(fd, &fds[1]);
182 ATF_REQUIRE_MSG(select(fd + 2, &fds[0], &fds[1], NULL, NULL) > 0,
183 FMT_ERR("select"));
184 if (FD_ISSET(fd, &fds[0])) {
185 ATF_REQUIRE_MSG(read(fd, buf, sizeof(buf)) > 0,
186 FMT_ERR("read"));
187 rd = true;
188 }
189 if (FD_ISSET(fd, &fds[1])) {
190 ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) > 0,
191 FMT_ERR("write"));
192 wr = true;
193 }
194 }
195 close(fd);
196 }
197
ATF_TP_ADD_TCS(tp)198 ATF_TP_ADD_TCS(tp)
199 {
200 ATF_TP_ADD_TC(tp, poll_kqueue);
201 ATF_TP_ADD_TC(tp, poll_poll);
202 ATF_TP_ADD_TC(tp, poll_select);
203
204 return (atf_no_error());
205 }
206