audiodev.c revision 1.10 1 /* $NetBSD: audiodev.c,v 1.10 2019/08/24 05:45:24 isaki 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 static unsigned int maxunit;
52
53 #define AUDIODEV_SAMPLE_RATE 44100
54
55 static int
56 audiodev_getinfo(struct audiodev *adev)
57 {
58 struct stat st;
59 struct audiofmt *f;
60 audio_format_query_t query;
61 int i;
62
63 if (stat(adev->ctlpath, &st) == -1)
64 return -1;
65 adev->dev = st.st_rdev;
66
67 if (stat(_PATH_AUDIOCTL, &st) != -1 && st.st_rdev == adev->dev)
68 adev->defaultdev = true;
69
70 adev->ctlfd = open(adev->ctlpath, O_RDONLY);
71 if (adev->ctlfd == -1) {
72 return -1;
73 }
74 if (ioctl(adev->ctlfd, AUDIO_GETDEV, &adev->audio_device) == -1) {
75 close(adev->ctlfd);
76 return -1;
77 }
78
79 for (i = 0; ;i++) {
80 memset(&query, 0, sizeof(query));
81 query.index = i;
82 if (ioctl(adev->ctlfd, AUDIO_QUERYFORMAT, &query) == -1) {
83 if (errno == ENODEV) {
84 /* QUERYFORMAT not supported. */
85 break;
86 }
87 if (errno == EINVAL)
88 break;
89 close(adev->ctlfd);
90 return -1;
91 }
92
93 f = calloc(1, sizeof(*f));
94 f->fmt = query.fmt;
95 TAILQ_INSERT_TAIL(&adev->formats, f, next);
96 }
97
98 if (ioctl(adev->ctlfd, AUDIO_GETFORMAT, &adev->hwinfo) == -1) {
99 close(adev->ctlfd);
100 return -1;
101 }
102
103 return 0;
104 }
105
106 static int
107 audiodev_add(const char *pdev, const char *dev, unsigned int unit)
108 {
109 struct audiodev *adev;
110
111 adev = calloc(1, sizeof(*adev));
112 if (adev == NULL)
113 return -1;
114
115 strlcpy(adev->pxname, pdev, sizeof(adev->pxname));
116 strlcpy(adev->xname, dev, sizeof(adev->xname));
117 snprintf(adev->path, sizeof(adev->path), "/dev/%s", dev);
118 snprintf(adev->ctlpath, sizeof(adev->ctlpath), "/dev/audioctl%d", unit);
119 adev->unit = unit;
120 TAILQ_INIT(&adev->formats);
121
122 if (audiodev_getinfo(adev) == -1) {
123 free(adev);
124 return -1;
125 }
126
127 #ifdef DEBUG
128 printf("DEBUG: [%c] %s(%s): %s\n", adev->defaultdev ? '*' : ' ',
129 adev->path, adev->ctlpath, adev->audio_device.name);
130 struct audiofmt *f;
131 TAILQ_FOREACH(f, &adev->formats, next) {
132 printf("DEBUG: enc%d, %d/%d, %dch\n",
133 f->fmt.encoding,
134 f->fmt.validbits,
135 f->fmt.precision,
136 f->fmt.channels);
137 }
138 #endif
139
140 TAILQ_INSERT_TAIL(&audiodevlist, adev, next);
141
142 if (unit > maxunit)
143 maxunit = unit;
144
145 return 0;
146 }
147
148 static void
149 audiodev_cb(void *args, const char *pdev, const char *dev, unsigned int unit)
150 {
151 audiodev_add(pdev, dev, unit);
152 }
153
154 int
155 audiodev_refresh(void)
156 {
157 struct audiodev *adev;
158 int fd, error;
159
160 fd = open(DRVCTLDEV, O_RDONLY);
161 if (fd == -1) {
162 perror("open " DRVCTLDEV);
163 return -1;
164 }
165
166 while (!TAILQ_EMPTY(&audiodevlist)) {
167 adev = TAILQ_FIRST(&audiodevlist);
168 if (adev->ctlfd != -1)
169 close(adev->ctlfd);
170 TAILQ_REMOVE(&audiodevlist, adev, next);
171 free(adev);
172 }
173
174 error = drvctl_foreach(fd, "audio", audiodev_cb, NULL);
175 if (error == -1) {
176 perror("drvctl");
177 return -1;
178 }
179
180 close(fd);
181
182 return 0;
183 }
184
185 unsigned int
186 audiodev_maxunit(void)
187 {
188 return maxunit;
189 }
190
191 /*
192 * Get audiodev corresponding to audio<i> device.
193 */
194 struct audiodev *
195 audiodev_get(unsigned int i)
196 {
197 struct audiodev *adev;
198
199 TAILQ_FOREACH(adev, &audiodevlist, next) {
200 if (i == adev->unit)
201 return adev;
202 }
203
204 return NULL;
205 }
206
207 int
208 audiodev_set_default(struct audiodev *adev)
209 {
210 char audiopath[PATH_MAX+1];
211 char soundpath[PATH_MAX+1];
212 char audioctlpath[PATH_MAX+1];
213 char mixerpath[PATH_MAX+1];
214
215 snprintf(audiopath, sizeof(audiopath),
216 _PATH_AUDIO "%u", adev->unit);
217 snprintf(soundpath, sizeof(soundpath),
218 _PATH_SOUND "%u", adev->unit);
219 snprintf(audioctlpath, sizeof(audioctlpath),
220 _PATH_AUDIOCTL "%u", adev->unit);
221 snprintf(mixerpath, sizeof(mixerpath),
222 _PATH_MIXER "%u", adev->unit);
223
224 unlink(_PATH_AUDIO);
225 unlink(_PATH_SOUND);
226 unlink(_PATH_AUDIOCTL);
227 unlink(_PATH_MIXER);
228
229 if (symlink(audiopath, _PATH_AUDIO) == -1) {
230 perror("symlink " _PATH_AUDIO);
231 return -1;
232 }
233 if (symlink(soundpath, _PATH_SOUND) == -1) {
234 perror("symlink " _PATH_SOUND);
235 return -1;
236 }
237 if (symlink(audioctlpath, _PATH_AUDIOCTL) == -1) {
238 perror("symlink " _PATH_AUDIOCTL);
239 return -1;
240 }
241 if (symlink(mixerpath, _PATH_MIXER) == -1) {
242 perror("symlink " _PATH_MIXER);
243 return -1;
244 }
245
246 return 0;
247 }
248
249 int
250 audiodev_set_param(struct audiodev *adev, int mode,
251 const char *encname, unsigned int prec, unsigned int ch, unsigned int freq)
252 {
253 audio_info_t ai;
254 int setmode;
255 u_int enc;
256
257 setmode = 0;
258 ai = adev->hwinfo;
259
260 for (enc = 0; enc < encoding_max; enc++) {
261 if (strcmp(encname, encoding_names[enc]) == 0)
262 break;
263 }
264 if (enc >= encoding_max) {
265 fprintf(stderr, "unknown encoding name: %s\n", encname);
266 errno = EINVAL;
267 return -1;
268 }
269
270 if ((ai.mode & mode & AUMODE_PLAY)) {
271 setmode |= AUMODE_PLAY;
272 ai.play.encoding = enc;
273 ai.play.precision = prec;
274 ai.play.channels = ch;
275 ai.play.sample_rate = freq;
276 }
277 if ((ai.mode & mode & AUMODE_RECORD)) {
278 setmode |= AUMODE_RECORD;
279 ai.record.encoding = enc;
280 ai.record.precision = prec;
281 ai.record.channels = ch;
282 ai.record.sample_rate = freq;
283 }
284
285 if (setmode == 0) {
286 errno = EINVAL;
287 return -1;
288 }
289
290 ai.mode = setmode;
291 printf("setting %s to %s:%u, %uch, %uHz\n",
292 adev->xname, encname, prec, ch, freq);
293 if (ioctl(adev->ctlfd, AUDIO_SETFORMAT, &ai) == -1) {
294 perror("ioctl AUDIO_SETFORMAT");
295 return -1;
296 }
297 return 0;
298 }
299
300 int
301 audiodev_test(struct audiodev *adev)
302 {
303 audio_info_t info;
304 unsigned int i;
305 int rv;
306
307 rv = -1;
308
309 adev->fd = open(adev->path, O_WRONLY);
310 if (adev->fd == -1) {
311 perror("open");
312 return -1;
313 }
314
315 AUDIO_INITINFO(&info);
316 info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
317 info.play.channels = adev->hwinfo.play.channels;
318 info.play.precision = 16;
319 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
320 info.mode = AUMODE_PLAY;
321 if (ioctl(adev->fd, AUDIO_SETINFO, &info) == -1) {
322 perror("ioctl AUDIO_SETINFO");
323 goto done;
324 }
325 if (ioctl(adev->fd, AUDIO_GETINFO, &info) == -1) {
326 perror("ioctl AUDIO_GETINFO");
327 goto done;
328 }
329
330 for (i = 0; i < adev->hwinfo.play.channels; i++) {
331 printf(" testing channel %u...", i);
332 fflush(stdout);
333 if (audiodev_test_chmask(adev, 1 << i, &info) == -1)
334 goto done;
335 printf(" done\n");
336 }
337
338 rv = 0;
339 done:
340 close(adev->fd);
341 return rv;
342 }
343
344 static int
345 audiodev_test_chmask(struct audiodev *adev, unsigned int chanmask,
346 audio_info_t *info)
347 {
348 int16_t *buf;
349 size_t buflen;
350 off_t off;
351 int rv;
352
353 rv = -1;
354
355 dtmf_new(&buf, &buflen, info->play.sample_rate, 2,
356 adev->hwinfo.play.channels, chanmask, 350.0, 440.0);
357 if (buf == NULL) {
358 return -1;
359 }
360
361 off = 0;
362 while (buflen > 0) {
363 size_t wlen;
364 ssize_t ret;
365
366 wlen = info->play.buffer_size;
367 if (wlen > buflen)
368 wlen = buflen;
369 ret = write(adev->fd, (char *)buf + off, wlen);
370 if (ret == -1) {
371 perror("write");
372 goto done;
373 }
374 wlen = ret;
375 off += wlen;
376 buflen -= wlen;
377 }
378
379 if (ioctl(adev->fd, AUDIO_DRAIN) == -1) {
380 perror("ioctl AUDIO_DRAIN");
381 goto done;
382 }
383
384 rv = 0;
385 done:
386 free(buf);
387 return rv;
388 }
389