• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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