audiodev.c revision 1.1 1 /* $NetBSD: audiodev.c,v 1.1 2010/08/30 02:19:47 mrg 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
44 static TAILQ_HEAD(audiodevhead, audiodev) audiodevlist =
45 TAILQ_HEAD_INITIALIZER(audiodevlist);
46
47 static int
48 audiodev_getinfo(struct audiodev *adev)
49 {
50 struct stat st;
51
52 if (stat(adev->path, &st) == -1)
53 return -1;
54 adev->dev = st.st_rdev;
55
56 if (stat(_PATH_AUDIO, &st) != -1 && st.st_rdev == adev->dev)
57 adev->defaultdev = true;
58
59 adev->fd = open(adev->path, O_RDWR);
60 if (adev->fd == -1)
61 return -1;
62 if (ioctl(adev->fd, AUDIO_GETDEV, &adev->audio_device) == -1) {
63 close(adev->fd);
64 return -1;
65 }
66
67 return 0;
68 }
69
70 static int
71 audiodev_add(const char *dev, unsigned int unit)
72 {
73 struct audiodev *adev;
74
75 adev = calloc(1, sizeof(*adev));
76 if (adev == NULL)
77 return -1;
78
79 strlcpy(adev->xname, dev, sizeof(adev->xname));
80 snprintf(adev->path, sizeof(adev->path) - 1, "/dev/%s", dev);
81 adev->unit = unit;
82
83 if (audiodev_getinfo(adev) == -1) {
84 free(adev);
85 return -1;
86 }
87
88 #ifdef DEBUG
89 printf("[%c] %s: %s\n", adev->defaultdev ? '*' : ' ',
90 adev->path, adev->audio_device.name);
91 #endif
92
93 TAILQ_INSERT_TAIL(&audiodevlist, adev, next);
94
95 return 0;
96 }
97
98 static void
99 audiodev_cb(void *args, const char *dev, unsigned int unit)
100 {
101 audiodev_add(dev, unit);
102 }
103
104 int
105 audiodev_refresh(void)
106 {
107 struct audiodev *adev;
108 int fd, error;
109
110 fd = open(DRVCTLDEV, O_RDWR);
111 if (fd == -1) {
112 perror("open " DRVCTLDEV);
113 return -1;
114 }
115
116 while (!TAILQ_EMPTY(&audiodevlist)) {
117 adev = TAILQ_FIRST(&audiodevlist);
118 if (adev->fd != -1)
119 close(adev->fd);
120 TAILQ_REMOVE(&audiodevlist, adev, next);
121 free(adev);
122 }
123
124 error = drvctl_foreach(fd, "audio", audiodev_cb, NULL);
125 if (error == -1) {
126 perror("drvctl");
127 return -1;
128 }
129
130 close(fd);
131
132 return 0;
133 }
134
135 unsigned int
136 audiodev_count(void)
137 {
138 struct audiodev *adev;
139 unsigned int n;
140
141 n = 0;
142 TAILQ_FOREACH(adev, &audiodevlist, next)
143 ++n;
144
145 return n;
146 }
147
148 struct audiodev *
149 audiodev_get(unsigned int i)
150 {
151 struct audiodev *adev;
152 unsigned int n;
153
154 n = 0;
155 TAILQ_FOREACH(adev, &audiodevlist, next) {
156 if (n == i)
157 return adev;
158 ++n;
159 }
160
161 return NULL;
162 }
163
164 int
165 audiodev_set_default(struct audiodev *adev)
166 {
167 char audiopath[PATH_MAX+1];
168 char soundpath[PATH_MAX+1];
169 char audioctlpath[PATH_MAX+1];
170 char mixerpath[PATH_MAX+1];
171
172 snprintf(audiopath, sizeof(audiopath) - 1,
173 _PATH_AUDIO "%u", adev->unit);
174 snprintf(soundpath, sizeof(soundpath) - 1,
175 _PATH_SOUND "%u", adev->unit);
176 snprintf(audioctlpath, sizeof(audioctlpath) - 1,
177 _PATH_AUDIOCTL "%u", adev->unit);
178 snprintf(mixerpath, sizeof(mixerpath) - 1,
179 _PATH_MIXER "%u", adev->unit);
180
181 unlink(_PATH_AUDIO);
182 unlink(_PATH_SOUND);
183 unlink(_PATH_AUDIOCTL);
184 unlink(_PATH_MIXER);
185
186 if (symlink(audiopath, _PATH_AUDIO) == -1) {
187 perror("symlink " _PATH_AUDIO);
188 return -1;
189 }
190 if (symlink(soundpath, _PATH_SOUND) == -1) {
191 perror("symlink " _PATH_SOUND);
192 return -1;
193 }
194 if (symlink(audioctlpath, _PATH_AUDIOCTL) == -1) {
195 perror("symlink " _PATH_AUDIOCTL);
196 return -1;
197 }
198 if (symlink(mixerpath, _PATH_MIXER) == -1) {
199 perror("symlink " _PATH_MIXER);
200 return -1;
201 }
202
203 return 0;
204 }
205