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 42PA_MODULE_AUTHOR("Lennart Poettering"); 43PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers"); 44PA_MODULE_VERSION(PACKAGE_VERSION); 45PA_MODULE_LOAD_ONCE(true); 46PA_MODULE_USAGE("just-one=<boolean>"); 47 48#ifdef __linux__ 49PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-detect!"); 50#endif 51 52static const char* const valid_modargs[] = { 53 "just-one", 54 NULL 55}; 56 57#ifdef HAVE_ALSA 58 59static 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 121static 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 211static 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 240static 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 253int 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 296fail: 297 if (ma) 298 pa_modargs_free(ma); 299 300 return -1; 301} 302