53c3edcfb029b2c177356711e32a148a33faccc0
[osspd.git] / osstest.c
1 /* Simple oss testsuite
2  *
3  * Copyright (C) 2009 Maarten Lankhorst <m.b.lankhorst@gmail.com>
4  *
5  * This file is released under the GPLv2.
6  */
7
8 #include <sys/types.h>
9 #include <sys/soundcard.h>
10 #include <sys/stat.h>
11 #include <sys/ioctl.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <poll.h>
19 #include <sys/mman.h>
20 #include <stdlib.h>
21
22 #define MIXERDEV "/dev/mixer"
23 #define DSPDEV "/dev/dsp"
24
25 /* Test macros */
26
27 static int errors, success;
28 static int report_success = 1;
29
30 #define ok(a, b, c...) do { \
31         if (!(a)) { \
32                 fprintf(stderr, "%s@%d test failed (%s): " b "\n", __func__, __LINE__, #a, ##c); \
33                 ++errors; \
34         } else { \
35                 if (report_success) \
36                         printf("%s@%d test succeeded (%s)\n", __func__, __LINE__, #a); \
37                 ++success; \
38         } } while (0)
39
40 static int mixerfd, dspfd;
41
42 static int reopen(int blocking)
43 {
44         close(dspfd);
45         if (!blocking)
46                 blocking = O_NDELAY;
47         else
48                 blocking = 0;
49         dspfd = open(DSPDEV, O_RDWR|blocking);
50         return dspfd;
51 }
52
53 static void test_ro(int fd)
54 {
55         int ret;
56         char buf[1024];
57         struct audio_buf_info abi;
58         memset(buf, 0, sizeof(buf));
59
60         ret = read(fd, buf, sizeof(buf));
61         ok(ret >= 0, "%s", strerror(errno));
62
63         ret = write(fd, buf, sizeof(buf));
64         ok(ret < 0, "read %d bytes", ret);
65
66         ret = ioctl(fd, SNDCTL_DSP_GETISPACE, &abi);
67         ok(ret >= 0, "%s", strerror(errno));
68
69         ret = ioctl(fd, SNDCTL_DSP_GETOSPACE, &abi);
70         ok(ret < 0, "%s", strerror(errno));
71         if (ret < 0)
72                 ok(errno == EINVAL, "Invalid errno: %s", strerror(errno));
73 }
74
75 static void test_wo(int fd)
76 {
77         int ret;
78         char buf[1024];
79         struct audio_buf_info abi;
80         memset(buf, 0, sizeof(buf));
81
82         ret = read(fd, buf, sizeof(buf));
83         ok(ret < 0, "read %d bytes", ret);
84
85         ret = write(fd, buf, sizeof(buf));
86         ok(ret >= 0, "%s", strerror(errno));
87
88         ret = ioctl(fd, SNDCTL_DSP_GETISPACE, &abi);
89         ok(ret < 0, "%s", strerror(errno));
90         if (ret < 0)
91                 ok(errno == EINVAL, "Invalid errno: %s", strerror(errno));
92
93         ret = ioctl(fd, SNDCTL_DSP_GETOSPACE, &abi);
94         ok(ret >= 0, "%s", strerror(errno));
95 }
96
97 static void test_rw(int fd)
98 {
99         int ret;
100         char buf[1024];
101         struct audio_buf_info abi;
102         memset(buf, 0, sizeof(buf));
103
104         ret = read(fd, buf, sizeof(buf));
105         ok(ret >= 0, "%s", strerror(errno));
106
107         ret = write(fd, buf, sizeof(buf));
108         ok(ret >= 0, "%s", strerror(errno));
109
110         ret = ioctl(fd, SNDCTL_DSP_GETISPACE, &abi);
111         ok(ret >= 0, "%s", strerror(errno));
112
113         ret = ioctl(fd, SNDCTL_DSP_GETOSPACE, &abi);
114         ok(ret >= 0, "%s", strerror(errno));
115 }
116
117 static void test_open(void)
118 {
119         int ro_fd, rw_fd, wo_fd;
120
121         mixerfd = open(MIXERDEV, O_RDONLY|O_NDELAY);
122         ok(mixerfd >= 0, "%s", strerror(errno));
123
124
125         /* In order to make this work it has to be serialized
126          * alsa's kernel emulation can only have device open once
127          * so do some specific smokescreen tests here
128          * and then open dsp for testing
129          */
130         ro_fd = open(DSPDEV, O_RDONLY);
131         ok(ro_fd >= 0, "%s", strerror(errno));
132
133         if (ro_fd >= 0)
134                 test_ro(ro_fd);
135
136         close(ro_fd);
137
138         wo_fd = open(DSPDEV, O_WRONLY);
139         ok(wo_fd >= 0, "%s", strerror(errno));
140
141         if (wo_fd >= 0)
142                 test_wo(wo_fd);
143
144         close(wo_fd);
145
146         rw_fd = open(DSPDEV, O_RDWR);
147         ok(rw_fd >= 0, "%s", strerror(errno));
148
149         if (rw_fd >= 0)
150                 test_rw(rw_fd);
151
152         dspfd = rw_fd;
153 }
154
155 static void test_mixer(void)
156 {
157         int ret;
158         struct mixer_info info;
159         memset(&info, 0, sizeof(info));
160
161         ret = ioctl(mixerfd, SOUND_MIXER_INFO, &info);
162         ok(ret >= 0, "%s", strerror(errno));
163         if (ret >= 0) {
164                 printf("Mixer id: %s\n", info.id);
165                 printf("Name: %s\n", info.name);
166         }
167 }
168
169 static void test_trigger(int fd)
170 {
171         int ret, i;
172
173         ret = ioctl(fd, SNDCTL_DSP_GETTRIGGER, &i);
174         ok(ret == 0, "Returned error %s", strerror(errno));
175         ok(i == (PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT), "i is set to %d", i);
176
177         i = 0;
178         ret = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &i);
179         ok(ret == 0, "Returned error %s", strerror(errno));
180         ok(i == 0,  "Wrong i returned");
181
182         i = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;
183         ret = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &i);
184         ok(ret == 0, "Returned error %s", strerror(errno));
185         ok(i == (PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT), "i has value %d", i);
186
187         ret = ioctl(fd, SNDCTL_DSP_POST, NULL);
188         ok(ret == 0, "Returned error %s", strerror(errno));
189 }
190
191 static void test_mmap(int fd)
192 {
193         char *area;
194         int ret;
195         char buf[24];
196
197         area = mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
198         ok(area != MAP_FAILED, "Failed to map: %s\n", strerror(errno));
199
200         if (area == MAP_FAILED)
201                 return;
202
203         ret = write(fd, &buf, sizeof(buf));
204         ok(ret == -1, "write after mmap returned %i\n", ret);
205         if (ret == -1)
206                 ok(errno == ENXIO, "Error returned is %s\n", strerror(errno));
207
208         munmap(area, 8192);
209 }
210
211 static void test_notify(int fd)
212 {
213         struct audio_buf_info bi;
214         char *bytes = NULL;
215         int ret, written;
216         struct pollfd pfd = { fd, POLLOUT };
217         int rounds = 20;
218
219         ioctl(fd, SNDCTL_DSP_GETOSPACE, &bi);
220
221         bytes = calloc(1, bi.fragsize);
222         written = 0;
223         ok(0, "Fragsize: %i, bytes: %i\n", bi.fragsize, bi.bytes);
224         while (written + bi.fragsize - 1 < bi.bytes)
225         {
226                 ret = write(fd, bytes, bi.fragsize);
227                 ok(ret == bi.fragsize, "Returned: %i instead of %i\n",
228                    ret, bi.fragsize);
229                 if (ret > 0)
230                         written += ret;
231         };
232
233         while (rounds--)
234         {
235                 ret = poll(&pfd, 1, -1);
236                 ok(ret > 0, "Poll returned %i\n", ret);
237                 if (ret < 0)
238                         break;
239                 ret = write(fd, bytes, bi.fragsize);
240                 if (ret < 0) ret = -errno;
241                 ok(ret == bi.fragsize, "Returned: %i instead of %i\n",
242                    ret, bi.fragsize);
243         }
244 }
245
246 int main()
247 {
248         test_open();
249         if (mixerfd >= 0)
250                 test_mixer();
251
252         if (reopen(1) >= 0)
253                 test_trigger(dspfd);
254
255         if (reopen(0) >= 0)
256                 test_notify(dspfd);
257
258         if (reopen(1) >= 0)
259                 test_mmap(dspfd);
260
261         close(mixerfd);
262         close(dspfd);
263         printf("Tests: %d errors %d success\n", errors, success);
264         return errors > 127 ? 127 : errors;
265 }
266