audiodev.c revision 1.4 1 /* $NetBSD: audiodev.c,v 1.4 2010/09/03 19:20:37 jmcneill 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 <fcntl.h>
35 #include <paths.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include "audiodev.h"
42 #include "drvctl.h"
43 #include "dtmf.h"
44
45 static TAILQ_HEAD(audiodevhead, audiodev) audiodevlist =
46 TAILQ_HEAD_INITIALIZER(audiodevlist);
47
48 #define AUDIODEV_SAMPLE_RATE 44100
49
50 static unsigned int
51 audiodev_probe_pchans(struct audiodev *adev)
52 {
53 audio_info_t info;
54 unsigned int nchans = 0, n;
55 int error;
56
57 AUDIO_INITINFO(&info);
58 info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
59 info.play.precision = 16;
60 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
61 info.play.channels = 1;
62 info.mode = AUMODE_PLAY;
63 error = ioctl(adev->fd, AUDIO_SETINFO, &info);
64 if (error == -1)
65 return 0;
66 nchans = 1;
67
68 for (n = 2; n <= 16; n += 2) {
69 info.play.channels = n;
70 error = ioctl(adev->fd, AUDIO_SETINFO, &info);
71 if (error == -1)
72 break;
73 nchans = info.play.channels;
74 }
75
76 return nchans;
77 }
78
79 static int
80 audiodev_getinfo(struct audiodev *adev)
81 {
82 struct stat st;
83
84 if (stat(adev->path, &st) == -1)
85 return -1;
86 adev->dev = st.st_rdev;
87
88 if (stat(_PATH_AUDIO, &st) != -1 && st.st_rdev == adev->dev)
89 adev->defaultdev = true;
90
91 adev->fd = open(adev->path, O_RDWR);
92 if (adev->fd == -1)
93 return -1;
94 if (ioctl(adev->fd, AUDIO_GETDEV, &adev->audio_device) == -1) {
95 close(adev->fd);
96 return -1;
97 }
98
99 adev->pchan = audiodev_probe_pchans(adev);
100
101 return 0;
102 }
103
104 static int
105 audiodev_add(const char *pdev, const char *dev, unsigned int unit)
106 {
107 struct audiodev *adev;
108
109 adev = calloc(1, sizeof(*adev));
110 if (adev == NULL)
111 return -1;
112
113 strlcpy(adev->pxname, pdev, sizeof(adev->pxname));
114 strlcpy(adev->xname, dev, sizeof(adev->xname));
115 snprintf(adev->path, sizeof(adev->path) - 1, "/dev/%s", dev);
116 adev->unit = unit;
117
118 if (audiodev_getinfo(adev) == -1) {
119 free(adev);
120 return -1;
121 }
122
123 #ifdef DEBUG
124 printf("[%c] %s: %s\n", adev->defaultdev ? '*' : ' ',
125 adev->path, adev->audio_device.name);
126 #endif
127
128 TAILQ_INSERT_TAIL(&audiodevlist, adev, next);
129
130 return 0;
131 }
132
133 static void
134 audiodev_cb(void *args, const char *pdev, const char *dev, unsigned int unit)
135 {
136 audiodev_add(pdev, dev, unit);
137 }
138
139 int
140 audiodev_refresh(void)
141 {
142 struct audiodev *adev;
143 int fd, error;
144
145 fd = open(DRVCTLDEV, O_RDONLY);
146 if (fd == -1) {
147 perror("open " DRVCTLDEV);
148 return -1;
149 }
150
151 while (!TAILQ_EMPTY(&audiodevlist)) {
152 adev = TAILQ_FIRST(&audiodevlist);
153 if (adev->fd != -1)
154 close(adev->fd);
155 TAILQ_REMOVE(&audiodevlist, adev, next);
156 free(adev);
157 }
158
159 error = drvctl_foreach(fd, "audio", audiodev_cb, NULL);
160 if (error == -1) {
161 perror("drvctl");
162 return -1;
163 }
164
165 close(fd);
166
167 return 0;
168 }
169
170 unsigned int
171 audiodev_count(void)
172 {
173 struct audiodev *adev;
174 unsigned int n;
175
176 n = 0;
177 TAILQ_FOREACH(adev, &audiodevlist, next)
178 ++n;
179
180 return n;
181 }
182
183 struct audiodev *
184 audiodev_get(unsigned int i)
185 {
186 struct audiodev *adev;
187 unsigned int n;
188
189 n = 0;
190 TAILQ_FOREACH(adev, &audiodevlist, next) {
191 if (n == i)
192 return adev;
193 ++n;
194 }
195
196 return NULL;
197 }
198
199 int
200 audiodev_set_default(struct audiodev *adev)
201 {
202 char audiopath[PATH_MAX+1];
203 char soundpath[PATH_MAX+1];
204 char audioctlpath[PATH_MAX+1];
205 char mixerpath[PATH_MAX+1];
206
207 snprintf(audiopath, sizeof(audiopath) - 1,
208 _PATH_AUDIO "%u", adev->unit);
209 snprintf(soundpath, sizeof(soundpath) - 1,
210 _PATH_SOUND "%u", adev->unit);
211 snprintf(audioctlpath, sizeof(audioctlpath) - 1,
212 _PATH_AUDIOCTL "%u", adev->unit);
213 snprintf(mixerpath, sizeof(mixerpath) - 1,
214 _PATH_MIXER "%u", adev->unit);
215
216 unlink(_PATH_AUDIO);
217 unlink(_PATH_SOUND);
218 unlink(_PATH_AUDIOCTL);
219 unlink(_PATH_MIXER);
220
221 if (symlink(audiopath, _PATH_AUDIO) == -1) {
222 perror("symlink " _PATH_AUDIO);
223 return -1;
224 }
225 if (symlink(soundpath, _PATH_SOUND) == -1) {
226 perror("symlink " _PATH_SOUND);
227 return -1;
228 }
229 if (symlink(audioctlpath, _PATH_AUDIOCTL) == -1) {
230 perror("symlink " _PATH_AUDIOCTL);
231 return -1;
232 }
233 if (symlink(mixerpath, _PATH_MIXER) == -1) {
234 perror("symlink " _PATH_MIXER);
235 return -1;
236 }
237
238 return 0;
239 }
240
241 int
242 audiodev_test(struct audiodev *adev, unsigned int chanmask)
243 {
244 audio_info_t info;
245 int16_t *buf;
246 size_t buflen;
247 off_t off;
248 int rv = 0;
249
250 AUDIO_INITINFO(&info);
251 info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
252 info.play.channels = adev->pchan;
253 info.play.precision = 16;
254 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
255 info.mode = AUMODE_PLAY;
256 if (ioctl(adev->fd, AUDIO_SETINFO, &info) == -1) {
257 perror("ioctl AUDIO_SETINFO");
258 return -1;
259 }
260 if (ioctl(adev->fd, AUDIO_GETINFO, &info) == -1) {
261 perror("ioctl AUDIO_GETINFO");
262 return -1;
263 }
264
265 dtmf_new(&buf, &buflen, info.play.sample_rate, 2,
266 adev->pchan, chanmask, 350.0, 440.0);
267 if (buf == NULL)
268 return -1;
269
270 off = 0;
271 while (buflen > 0) {
272 size_t wlen = info.play.buffer_size;
273 if (wlen > buflen)
274 wlen = buflen;
275 wlen = write(adev->fd, (char *)buf + off, wlen);
276 if (wlen == -1) {
277 perror("write");
278 rv = -1;
279 goto done;
280 }
281 off += wlen;
282 buflen -= wlen;
283 }
284
285 if (ioctl(adev->fd, AUDIO_DRAIN) == -1)
286 perror("ioctl AUDIO_DRAIN");
287
288 done:
289 free(buf);
290
291 return rv;
292 }
293