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 /* FreeBSD support, the devices are named /dev/dsp0.0, dsp0.1 and so on */
164 pa_snprintf(args, sizeof(args), "device=/dev/dsp%u.0", device);
165
166 if (pa_module_load(&m, c, "module-oss", args) < 0)
167 continue;
168 }
169
170 n++;
171
172 if (just_one)
173 break;
174 }
175
176 fclose(f);
177 return n;
178 }
179 #endif
180
181 #ifdef HAVE_SOLARIS
detect_solaris(pa_core * c,int just_one)182 static int detect_solaris(pa_core *c, int just_one) {
183 struct stat s;
184 const char *dev;
185 char args[64];
186 pa_module *m = NULL;
187
188 dev = getenv("AUDIODEV");
189 if (!dev)
190 dev = "/dev/audio";
191
192 if (stat(dev, &s) < 0) {
193 if (errno != ENOENT)
194 pa_log_error("failed to open device %s: %s", dev, pa_cstrerror(errno));
195 return -1;
196 }
197
198 if (!S_ISCHR(s.st_mode))
199 return 0;
200
201 pa_snprintf(args, sizeof(args), "device=%s", dev);
202
203 if (pa_module_load(&m, c, "module-solaris", args) < 0)
204 return 0;
205
206 return 1;
207 }
208 #endif
209
210 #ifdef OS_IS_WIN32
detect_waveout(pa_core * c,int just_one)211 static int detect_waveout(pa_core *c, int just_one) {
212 pa_module *m = NULL;
213 /*
214 * FIXME: No point in enumerating devices until the plugin supports
215 * selecting anything but the first.
216 */
217 if (pa_module_load(&m, c, "module-waveout", "") < 0)
218 return 0;
219
220 return 1;
221 }
222 #endif
223
pa__init(pa_module * m)224 int pa__init(pa_module*m) {
225 bool just_one = false;
226 int n = 0;
227 pa_modargs *ma;
228
229 pa_assert(m);
230
231 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
232 pa_log("Failed to parse module arguments");
233 goto fail;
234 }
235
236 if (pa_modargs_get_value_boolean(ma, "just-one", &just_one) < 0) {
237 pa_log("just_one= expects a boolean argument.");
238 goto fail;
239 }
240
241 #ifdef HAVE_ALSA
242 if ((n = detect_alsa(m->core, just_one)) <= 0)
243 #endif
244 #ifdef HAVE_OSS_OUTPUT
245 if ((n = detect_oss(m->core, just_one)) <= 0)
246 #endif
247 #ifdef HAVE_SOLARIS
248 if ((n = detect_solaris(m->core, just_one)) <= 0)
249 #endif
250 #ifdef OS_IS_WIN32
251 if ((n = detect_waveout(m->core, just_one)) <= 0)
252 #endif
253 {
254 pa_log_warn("failed to detect any sound hardware.");
255 goto fail;
256 }
257
258 pa_log_info("loaded %i modules.", n);
259
260 /* We were successful and can unload ourselves now. */
261 pa_module_unload_request(m, true);
262
263 pa_modargs_free(ma);
264
265 return 0;
266
267 fail:
268 if (ma)
269 pa_modargs_free(ma);
270
271 return -1;
272 }
273