1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6 Copyright 2006 Diego Pettenò
7
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2.1 of the License,
11 or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 #include <pulsecore/core-error.h>
36 #include <pulsecore/module.h>
37 #include <pulsecore/modargs.h>
38 #include <pulsecore/log.h>
39 #include <pulsecore/core-util.h>
40 #include <pulsecore/macro.h>
41
42 PA_MODULE_AUTHOR("Lennart Poettering");
43 PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
44 PA_MODULE_VERSION(PACKAGE_VERSION);
45 PA_MODULE_LOAD_ONCE(true);
46 PA_MODULE_USAGE("just-one=<boolean>");
47
48 #ifdef __linux__
49 PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-detect!");
50 #endif
51
52 static const char* const valid_modargs[] = {
53 "just-one",
54 NULL
55 };
56
57 #ifdef HAVE_ALSA
58
detect_alsa(pa_core * c,int just_one)59 static int detect_alsa(pa_core *c, int just_one) {
60 FILE *f;
61 int n = 0, n_sink = 0, n_source = 0;
62
63 if (!(f = pa_fopen_cloexec("/proc/asound/devices", "r"))) {
64
65 if (errno != ENOENT)
66 pa_log_error("open(\"/proc/asound/devices\") failed: %s", pa_cstrerror(errno));
67
68 return -1;
69 }
70
71 while (!feof(f)) {
72 char line[64], args[64];
73 unsigned device, subdevice;
74 int is_sink;
75 pa_module *m = NULL;
76
77 if (!fgets(line, sizeof(line), f))
78 break;
79
80 line[strcspn(line, "\r\n")] = 0;
81
82 if (pa_endswith(line, "digital audio playback"))
83 is_sink = 1;
84 else if (pa_endswith(line, "digital audio capture"))
85 is_sink = 0;
86 else
87 continue;
88
89 if (just_one && is_sink && n_sink >= 1)
90 continue;
91
92 if (just_one && !is_sink && n_source >= 1)
93 continue;
94
95 if (sscanf(line, " %*i: [%u- %u]: ", &device, &subdevice) != 2)
96 continue;
97
98 /* Only one sink per device */
99 if (subdevice != 0)
100 continue;
101
102 pa_snprintf(args, sizeof(args), "device_id=%u", device);
103 if (pa_module_load(&m, c, is_sink ? "module-alsa-sink" : "module-alsa-source", args) < 0)
104 continue;
105
106 n++;
107
108 if (is_sink)
109 n_sink++;
110 else
111 n_source++;
112 }
113
114 fclose(f);
115
116 return n;
117 }
118 #endif
119
120 #ifdef HAVE_OSS_OUTPUT
detect_oss(pa_core * c,int just_one)121 static int detect_oss(pa_core *c, int just_one) {
122 FILE *f;
123 int n = 0, b = 0;
124
125 if (!(f = pa_fopen_cloexec("/dev/sndstat", "r")) &&
126 !(f = pa_fopen_cloexec("/proc/sndstat", "r")) &&
127 !(f = pa_fopen_cloexec("/proc/asound/oss/sndstat", "r"))) {
128
129 if (errno != ENOENT)
130 pa_log_error("failed to open OSS sndstat device: %s", pa_cstrerror(errno));
131
132 return -1;
133 }
134
135 while (!feof(f)) {
136 char line[256], args[64];
137 unsigned device;
138 pa_module *m = NULL;
139
140 if (!fgets(line, sizeof(line), f))
141 break;
142
143 line[strcspn(line, "\r\n")] = 0;
144
145 if (!b) {
146 b = pa_streq(line, "Audio devices:") || pa_streq(line, "Installed devices:");
147 continue;
148 }
149
150 if (line[0] == 0)
151 break;
152
153 if (sscanf(line, "%u: ", &device) == 1) {
154 if (device == 0)
155 pa_snprintf(args, sizeof(args), "device=/dev/dsp");
156 else
157 pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", device);
158
159 if (pa_module_load(&m, c, "module-oss", args) < 0)
160 continue;
161
162 } else if (sscanf(line, "pcm%u: ", &device) == 1) {
163 pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", device);
164
165 if (pa_module_load(&m, c, "module-oss", args) < 0)
166 continue;
167
168 if (!pa_endswith(line, "default"))
169 continue;
170
171 const char *p = strrchr(line, '(');
172
173 if (!p)
174 continue;
175
176 if (!c->configured_default_sink && (strstr(p, "play") || (strstr(p, "p:") && !strstr(p, "(0p:")))) {
177 uint32_t idx = PA_IDXSET_INVALID;
178 pa_sink *s;
179 PA_IDXSET_FOREACH(s, c->sinks, idx) {
180 if (s->module == m) {
181 pa_core_set_configured_default_sink(c, s->name);
182 break;
183 }
184 }
185 }
186
187 if (!c->configured_default_source && (strstr(p, "rec") || (strstr(p, "r:") && !strstr(p, "/0r:")))) {
188 uint32_t idx = PA_IDXSET_INVALID;
189 pa_source *s;
190 PA_IDXSET_FOREACH(s, c->sources, idx) {
191 if (s->module == m) {
192 pa_core_set_configured_default_source(c, s->name);
193 break;
194 }
195 }
196 }
197 }
198
199 n++;
200
201 if (just_one)
202 break;
203 }
204
205 fclose(f);
206 return n;
207 }
208 #endif
209
210 #ifdef HAVE_SOLARIS
detect_solaris(pa_core * c,int just_one)211 static int detect_solaris(pa_core *c, int just_one) {
212 struct stat s;
213 const char *dev;
214 char args[64];
215 pa_module *m = NULL;
216
217 dev = getenv("AUDIODEV");
218 if (!dev)
219 dev = "/dev/audio";
220
221 if (stat(dev, &s) < 0) {
222 if (errno != ENOENT)
223 pa_log_error("failed to open device %s: %s", dev, pa_cstrerror(errno));
224 return -1;
225 }
226
227 if (!S_ISCHR(s.st_mode))
228 return 0;
229
230 pa_snprintf(args, sizeof(args), "device=%s", dev);
231
232 if (pa_module_load(&m, c, "module-solaris", args) < 0)
233 return 0;
234
235 return 1;
236 }
237 #endif
238
239 #ifdef OS_IS_WIN32
detect_waveout(pa_core * c,int just_one)240 static int detect_waveout(pa_core *c, int just_one) {
241 pa_module *m = NULL;
242 /*
243 * FIXME: No point in enumerating devices until the plugin supports
244 * selecting anything but the first.
245 */
246 if (pa_module_load(&m, c, "module-waveout", "") < 0)
247 return 0;
248
249 return 1;
250 }
251 #endif
252
pa__init(pa_module * m)253 int pa__init(pa_module*m) {
254 bool just_one = false;
255 int n = 0;
256 pa_modargs *ma;
257
258 pa_assert(m);
259
260 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
261 pa_log("Failed to parse module arguments");
262 goto fail;
263 }
264
265 if (pa_modargs_get_value_boolean(ma, "just-one", &just_one) < 0) {
266 pa_log("just_one= expects a boolean argument.");
267 goto fail;
268 }
269
270 #ifdef HAVE_ALSA
271 if ((n = detect_alsa(m->core, just_one)) <= 0)
272 #endif
273 #ifdef HAVE_OSS_OUTPUT
274 if ((n = detect_oss(m->core, just_one)) <= 0)
275 #endif
276 #ifdef HAVE_SOLARIS
277 if ((n = detect_solaris(m->core, just_one)) <= 0)
278 #endif
279 #ifdef OS_IS_WIN32
280 if ((n = detect_waveout(m->core, just_one)) <= 0)
281 #endif
282 {
283 pa_log_warn("failed to detect any sound hardware.");
284 goto fail;
285 }
286
287 pa_log_info("loaded %i modules.", n);
288
289 /* We were successful and can unload ourselves now. */
290 pa_module_unload_request(m, true);
291
292 pa_modargs_free(ma);
293
294 return 0;
295
296 fail:
297 if (ma)
298 pa_modargs_free(ma);
299
300 return -1;
301 }
302