audiodev.c revision 1.7.2.1 1 /* $NetBSD: audiodev.c,v 1.7.2.1 2019/09/28 07:41:18 martin Exp $ */
2
3 /*
4 * Copyright (c) 2010 Jared D. McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/queue.h>
30 #include <sys/ioctl.h>
31 #include <sys/stat.h>
32 #include <sys/drvctlio.h>
33
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <paths.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 #include "audiodev.h"
43 #include "drvctl.h"
44 #include "dtmf.h"
45
46 static int audiodev_test_chmask(struct audiodev *, unsigned int,
47 audio_info_t *);
48
49 static TAILQ_HEAD(audiodevhead, audiodev) audiodevlist =
50 TAILQ_HEAD_INITIALIZER(audiodevlist);
51
52 #define AUDIODEV_SAMPLE_RATE 44100
53
54 static int
55 audiodev_getinfo(struct audiodev *adev)
56 {
57 struct stat st;
58 struct audiofmt *f;
59 audio_format_query_t query;
60 int i;
61
62 if (stat(adev->ctlpath, &st) == -1)
63 return -1;
64 adev->dev = st.st_rdev;
65
66 if (stat(_PATH_AUDIOCTL, &st) != -1 && st.st_rdev == adev->dev)
67 adev->defaultdev = true;
68
69 adev->ctlfd = open(adev->ctlpath, O_RDONLY);
70 if (adev->ctlfd == -1) {
71 return -1;
72 }
73 if (ioctl(adev->ctlfd, AUDIO_GETDEV, &adev->audio_device) == -1) {
74 close(adev->ctlfd);
75 return -1;
76 }
77
78 for (i = 0; ;i++) {
79 memset(&query, 0, sizeof(query));
80 query.index = i;
81 if (ioctl(adev->ctlfd, AUDIO_QUERYFORMAT, &query) == -1) {
82 if (errno == ENODEV) {
83 /* QUERYFORMAT not supported. */
84 break;
85 }
86 if (errno == EINVAL)
87 break;
88 close(adev->ctlfd);
89 return -1;
90 }
91
92 f = calloc(1, sizeof(*f));
93 f->fmt = query.fmt;
94 TAILQ_INSERT_TAIL(&adev->formats, f, next);
95 }
96
97 if (ioctl(adev->ctlfd, AUDIO_GETFORMAT, &adev->hwinfo) == -1) {
98 close(adev->ctlfd);
99 return -1;
100 }
101
102 return 0;
103 }
104
105 static int
106 audiodev_add(const char *pdev, const char *dev, unsigned int unit)
107 {
108 struct audiodev *adev;
109
110 adev = calloc(1, sizeof(*adev));
111 if (adev == NULL)
112 return -1;
113
114 strlcpy(adev->pxname, pdev, sizeof(adev->pxname));
115 strlcpy(adev->xname, dev, sizeof(adev->xname));
116 snprintf(adev->path, sizeof(adev->path), "/dev/%s", dev);
117 snprintf(adev->ctlpath, sizeof(adev->ctlpath), "/dev/audioctl%d", unit);
118 adev->unit = unit;
119 TAILQ_INIT(&adev->formats);
120
121 if (audiodev_getinfo(adev) == -1) {
122 free(adev);
123 return -1;
124 }
125
126 #ifdef DEBUG
127 printf("DEBUG: [%c] %s(%s): %s\n", adev->defaultdev ? '*' : ' ',
128 adev->path, adev->ctlpath, adev->audio_device.name);
129 struct audiofmt *f;
130 TAILQ_FOREACH(f, &adev->formats, next) {
131 printf("DEBUG: enc%d, %d/%d, %dch\n",
132 f->fmt.encoding,
133 f->fmt.validbits,
134 f->fmt.precision,
135 f->fmt.channels);
136 }
137 #endif
138
139 TAILQ_INSERT_TAIL(&audiodevlist, adev, next);
140
141 return 0;
142 }
143
144 static void
145 audiodev_cb(void *args, const char *pdev, const char *dev, unsigned int unit)
146 {
147 audiodev_add(pdev, dev, unit);
148 }
149
150 int
151 audiodev_refresh(void)
152 {
153 struct audiodev *adev;
154 int fd, error;
155
156 fd = open(DRVCTLDEV, O_RDONLY);
157 if (fd == -1) {
158 perror("open " DRVCTLDEV);
159 return -1;
160 }
161
162 while (!TAILQ_EMPTY(&audiodevlist)) {
163 adev = TAILQ_FIRST(&audiodevlist);
164 if (adev->ctlfd != -1)
165 close(adev->ctlfd);
166 TAILQ_REMOVE(&audiodevlist, adev, next);
167 free(adev);
168 }
169
170 error = drvctl_foreach(fd, "audio", audiodev_cb, NULL);
171 if (error == -1) {
172 perror("drvctl");
173 return -1;
174 }
175
176 close(fd);
177
178 return 0;
179 }
180
181 unsigned int
182 audiodev_count(void)
183 {
184 struct audiodev *adev;
185 unsigned int n;
186
187 n = 0;
188 TAILQ_FOREACH(adev, &audiodevlist, next)
189 ++n;
190
191 return n;
192 }
193
194 struct audiodev *
195 audiodev_get(unsigned int i)
196 {
197 struct audiodev *adev;
198 unsigned int n;
199
200 n = 0;
201 TAILQ_FOREACH(adev, &audiodevlist, next) {
202 if (n == i)
203 return adev;
204 ++n;
205 }
206
207 return NULL;
208 }
209
210 int
211 audiodev_set_default(struct audiodev *adev)
212 {
213 char audiopath[PATH_MAX+1];
214 char soundpath[PATH_MAX+1];
215 char audioctlpath[PATH_MAX+1];
216 char mixerpath[PATH_MAX+1];
217
218 snprintf(audiopath, sizeof(audiopath),
219 _PATH_AUDIO "%u", adev->unit);
220 snprintf(soundpath, sizeof(soundpath),
221 _PATH_SOUND "%u", adev->unit);
222 snprintf(audioctlpath, sizeof(audioctlpath),
223 _PATH_AUDIOCTL "%u", adev->unit);
224 snprintf(mixerpath, sizeof(mixerpath),
225 _PATH_MIXER "%u", adev->unit);
226
227 unlink(_PATH_AUDIO);
228 unlink(_PATH_SOUND);
229 unlink(_PATH_AUDIOCTL);
230 unlink(_PATH_MIXER);
231
232 if (symlink(audiopath, _PATH_AUDIO) == -1) {
233 perror("symlink " _PATH_AUDIO);
234 return -1;
235 }
236 if (symlink(soundpath, _PATH_SOUND) == -1) {
237 perror("symlink " _PATH_SOUND);
238 return -1;
239 }
240 if (symlink(audioctlpath, _PATH_AUDIOCTL) == -1) {
241 perror("symlink " _PATH_AUDIOCTL);
242 return -1;
243 }
244 if (symlink(mixerpath, _PATH_MIXER) == -1) {
245 perror("symlink " _PATH_MIXER);
246 return -1;
247 }
248
249 return 0;
250 }
251
252 int
253 audiodev_set_param(struct audiodev *adev, int mode,
254 const char *encname, unsigned int prec, unsigned int ch, unsigned int freq)
255 {
256 audio_info_t ai;
257 int setmode;
258 u_int enc;
259
260 setmode = 0;
261 ai = adev->hwinfo;
262
263 for (enc = 0; enc < encoding_max; enc++) {
264 if (strcmp(encname, encoding_names[enc]) == 0)
265 break;
266 }
267 if (enc >= encoding_max) {
268 fprintf(stderr, "unknown encoding name: %s\n", encname);
269 errno = EINVAL;
270 return -1;
271 }
272
273 if ((ai.mode & mode & AUMODE_PLAY)) {
274 setmode |= AUMODE_PLAY;
275 ai.play.encoding = enc;
276 ai.play.precision = prec;
277 ai.play.channels = ch;
278 ai.play.sample_rate = freq;
279 }
280 if ((ai.mode & mode & AUMODE_RECORD)) {
281 setmode |= AUMODE_RECORD;
282 ai.record.encoding = enc;
283 ai.record.precision = prec;
284 ai.record.channels = ch;
285 ai.record.sample_rate = freq;
286 }
287
288 if (setmode == 0) {
289 errno = EINVAL;
290 return -1;
291 }
292
293 ai.mode = setmode;
294 printf("setting %s to %s:%u, %uch, %uHz\n",
295 adev->xname, encname, prec, ch, freq);
296 if (ioctl(adev->ctlfd, AUDIO_SETFORMAT, &ai) == -1) {
297 perror("ioctl AUDIO_SETFORMAT");
298 return -1;
299 }
300 return 0;
301 }
302
303 int
304 audiodev_test(struct audiodev *adev)
305 {
306 audio_info_t info;
307 unsigned int i;
308 int rv;
309
310 rv = -1;
311
312 adev->fd = open(adev->path, O_WRONLY);
313 if (adev->fd == -1) {
314 perror("open");
315 return -1;
316 }
317
318 AUDIO_INITINFO(&info);
319 info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
320 info.play.channels = adev->hwinfo.play.channels;
321 info.play.precision = 16;
322 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
323 info.mode = AUMODE_PLAY;
324 if (ioctl(adev->fd, AUDIO_SETINFO, &info) == -1) {
325 perror("ioctl AUDIO_SETINFO");
326 goto done;
327 }
328 if (ioctl(adev->fd, AUDIO_GETINFO, &info) == -1) {
329 perror("ioctl AUDIO_GETINFO");
330 goto done;
331 }
332
333 for (i = 0; i < adev->hwinfo.play.channels; i++) {
334 printf(" testing channel %u...", i);
335 fflush(stdout);
336 if (audiodev_test_chmask(adev, 1 << i, &info) == -1)
337 goto done;
338 printf(" done\n");
339 }
340
341 rv = 0;
342 done:
343 close(adev->fd);
344 return rv;
345 }
346
347 static int
348 audiodev_test_chmask(struct audiodev *adev, unsigned int chanmask,
349 audio_info_t *info)
350 {
351 int16_t *buf;
352 size_t buflen;
353 off_t off;
354 int rv;
355
356 rv = -1;
357
358 dtmf_new(&buf, &buflen, info->play.sample_rate, 2,
359 adev->hwinfo.play.channels, chanmask, 350.0, 440.0);
360 if (buf == NULL) {
361 return -1;
362 }
363
364 off = 0;
365 while (buflen > 0) {
366 size_t wlen;
367 ssize_t ret;
368
369 wlen = info->play.buffer_size;
370 if (wlen > buflen)
371 wlen = buflen;
372 ret = write(adev->fd, (char *)buf + off, wlen);
373 if (ret == -1) {
374 perror("write");
375 goto done;
376 }
377 wlen = ret;
378 off += wlen;
379 buflen -= wlen;
380 }
381
382 if (ioctl(adev->fd, AUDIO_DRAIN) == -1) {
383 perror("ioctl AUDIO_DRAIN");
384 goto done;
385 }
386
387 rv = 0;
388 done:
389 free(buf);
390 return rv;
391 }
392