/*** This file is part of PulseAudio. Copyright 2006 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB Copyright 2006 Diego Pettenò PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, see . ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include PA_MODULE_AUTHOR("Lennart Poettering"); PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(true); PA_MODULE_USAGE("just-one="); #ifdef __linux__ PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-detect!"); #endif static const char* const valid_modargs[] = { "just-one", NULL }; #ifdef HAVE_ALSA static int detect_alsa(pa_core *c, int just_one) { FILE *f; int n = 0, n_sink = 0, n_source = 0; if (!(f = pa_fopen_cloexec("/proc/asound/devices", "r"))) { if (errno != ENOENT) pa_log_error("open(\"/proc/asound/devices\") failed: %s", pa_cstrerror(errno)); return -1; } while (!feof(f)) { char line[64], args[64]; unsigned device, subdevice; int is_sink; pa_module *m = NULL; if (!fgets(line, sizeof(line), f)) break; line[strcspn(line, "\r\n")] = 0; if (pa_endswith(line, "digital audio playback")) is_sink = 1; else if (pa_endswith(line, "digital audio capture")) is_sink = 0; else continue; if (just_one && is_sink && n_sink >= 1) continue; if (just_one && !is_sink && n_source >= 1) continue; if (sscanf(line, " %*i: [%u- %u]: ", &device, &subdevice) != 2) continue; /* Only one sink per device */ if (subdevice != 0) continue; pa_snprintf(args, sizeof(args), "device_id=%u", device); if (pa_module_load(&m, c, is_sink ? "module-alsa-sink" : "module-alsa-source", args) < 0) continue; n++; if (is_sink) n_sink++; else n_source++; } fclose(f); return n; } #endif #ifdef HAVE_OSS_OUTPUT static int detect_oss(pa_core *c, int just_one) { FILE *f; int n = 0, b = 0; if (!(f = pa_fopen_cloexec("/dev/sndstat", "r")) && !(f = pa_fopen_cloexec("/proc/sndstat", "r")) && !(f = pa_fopen_cloexec("/proc/asound/oss/sndstat", "r"))) { if (errno != ENOENT) pa_log_error("failed to open OSS sndstat device: %s", pa_cstrerror(errno)); return -1; } while (!feof(f)) { char line[256], args[64]; unsigned device; pa_module *m = NULL; if (!fgets(line, sizeof(line), f)) break; line[strcspn(line, "\r\n")] = 0; if (!b) { b = pa_streq(line, "Audio devices:") || pa_streq(line, "Installed devices:"); continue; } if (line[0] == 0) break; if (sscanf(line, "%u: ", &device) == 1) { if (device == 0) pa_snprintf(args, sizeof(args), "device=/dev/dsp"); else pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", device); if (pa_module_load(&m, c, "module-oss", args) < 0) continue; } else if (sscanf(line, "pcm%u: ", &device) == 1) { pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", device); if (pa_module_load(&m, c, "module-oss", args) < 0) continue; if (!pa_endswith(line, "default")) continue; const char *p = strrchr(line, '('); if (!p) continue; if (!c->configured_default_sink && (strstr(p, "play") || (strstr(p, "p:") && !strstr(p, "(0p:")))) { uint32_t idx = PA_IDXSET_INVALID; pa_sink *s; PA_IDXSET_FOREACH(s, c->sinks, idx) { if (s->module == m) { pa_core_set_configured_default_sink(c, s->name); break; } } } if (!c->configured_default_source && (strstr(p, "rec") || (strstr(p, "r:") && !strstr(p, "/0r:")))) { uint32_t idx = PA_IDXSET_INVALID; pa_source *s; PA_IDXSET_FOREACH(s, c->sources, idx) { if (s->module == m) { pa_core_set_configured_default_source(c, s->name); break; } } } } n++; if (just_one) break; } fclose(f); return n; } #endif #ifdef HAVE_SOLARIS static int detect_solaris(pa_core *c, int just_one) { struct stat s; const char *dev; char args[64]; pa_module *m = NULL; dev = getenv("AUDIODEV"); if (!dev) dev = "/dev/audio"; if (stat(dev, &s) < 0) { if (errno != ENOENT) pa_log_error("failed to open device %s: %s", dev, pa_cstrerror(errno)); return -1; } if (!S_ISCHR(s.st_mode)) return 0; pa_snprintf(args, sizeof(args), "device=%s", dev); if (pa_module_load(&m, c, "module-solaris", args) < 0) return 0; return 1; } #endif #ifdef OS_IS_WIN32 static int detect_waveout(pa_core *c, int just_one) { pa_module *m = NULL; /* * FIXME: No point in enumerating devices until the plugin supports * selecting anything but the first. */ if (pa_module_load(&m, c, "module-waveout", "") < 0) return 0; return 1; } #endif int pa__init(pa_module*m) { bool just_one = false; int n = 0; pa_modargs *ma; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; } if (pa_modargs_get_value_boolean(ma, "just-one", &just_one) < 0) { pa_log("just_one= expects a boolean argument."); goto fail; } #ifdef HAVE_ALSA if ((n = detect_alsa(m->core, just_one)) <= 0) #endif #ifdef HAVE_OSS_OUTPUT if ((n = detect_oss(m->core, just_one)) <= 0) #endif #ifdef HAVE_SOLARIS if ((n = detect_solaris(m->core, just_one)) <= 0) #endif #ifdef OS_IS_WIN32 if ((n = detect_waveout(m->core, just_one)) <= 0) #endif { pa_log_warn("failed to detect any sound hardware."); goto fail; } pa_log_info("loaded %i modules.", n); /* We were successful and can unload ourselves now. */ pa_module_unload_request(m, true); pa_modargs_free(ma); return 0; fail: if (ma) pa_modargs_free(ma); return -1; }