summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2009-08-22 23:10:45 +0200
committerLennart Poettering <lennart@poettering.net>2009-08-22 23:10:45 +0200
commitd6fb8d10819bebc1cee203de7330cceeafde9fed (patch)
treea9dc96e3d061586127922f1a5d33a03e7c51879b /src
parent560da5b0a1386c2a158d69b6ba0ef99c0f03bbf4 (diff)
udev: check busy status of alsa cards before loading alsa modules and hence initiating profile probing
Diffstat (limited to 'src')
-rw-r--r--src/modules/module-udev-detect.c149
1 files changed, 132 insertions, 17 deletions
diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c
index 55136348..b41b9c0f 100644
--- a/src/modules/module-udev-detect.c
+++ b/src/modules/module-udev-detect.c
@@ -25,6 +25,7 @@
#include <errno.h>
#include <limits.h>
+#include <dirent.h>
#include <sys/inotify.h>
#include <libudev.h>
@@ -45,8 +46,7 @@ PA_MODULE_USAGE(
struct device {
char *path;
- pa_bool_t accessible:1;
- pa_bool_t need_verify:1;
+ pa_bool_t need_verify;
char *card_name;
char *args;
uint32_t module;
@@ -99,34 +99,150 @@ static const char *path_get_card_id(const char *path) {
return e + 5;
}
+static pa_bool_t is_card_busy(const char *id) {
+ char *card_path = NULL, *pcm_path = NULL, *sub_status = NULL;
+ DIR *card_dir = NULL, *pcm_dir = NULL;
+ FILE *status_file = NULL;
+ size_t len;
+ struct dirent *space = NULL, *de;
+ pa_bool_t busy = FALSE;
+ int r;
+
+ pa_assert(id);
+
+ card_path = pa_sprintf_malloc("/proc/asound/card%s", id);
+
+ if (!(card_dir = opendir(card_path))) {
+ pa_log_warn("Failed to open %s: %s", card_path, pa_cstrerror(errno));
+ goto fail;
+ }
+
+ len = offsetof(struct dirent, d_name) + fpathconf(dirfd(card_dir), _PC_NAME_MAX) + 1;
+ space = pa_xmalloc(len);
+
+ for (;;) {
+ de = NULL;
+
+ if ((r = readdir_r(card_dir, space, &de)) != 0) {
+ pa_log_warn("readdir_r() failed: %s", pa_cstrerror(r));
+ goto fail;
+ }
+
+ if (!de)
+ break;
+
+ if (!pa_startswith(de->d_name, "pcm"))
+ continue;
+
+ pa_xfree(pcm_path);
+ pcm_path = pa_sprintf_malloc("%s/%s", card_path, de->d_name);
+
+ if (pcm_dir)
+ closedir(pcm_dir);
+
+ if (!(pcm_dir = opendir(pcm_path))) {
+ pa_log_warn("Failed to open %s: %s", pcm_path, pa_cstrerror(errno));
+ continue;
+ }
+
+ for (;;) {
+ char line[32];
+
+ if ((r = readdir_r(pcm_dir, space, &de)) != 0) {
+ pa_log_warn("readdir_r() failed: %s", pa_cstrerror(r));
+ goto fail;
+ }
+
+ if (!de)
+ break;
+
+ if (!pa_startswith(de->d_name, "sub"))
+ continue;
+
+ pa_xfree(sub_status);
+ sub_status = pa_sprintf_malloc("%s/%s/status", pcm_path, de->d_name);
+
+ if (status_file)
+ fclose(status_file);
+
+ if (!(status_file = fopen(sub_status, "r"))) {
+ pa_log_warn("Failed to open %s: %s", sub_status, pa_cstrerror(errno));
+ continue;
+ }
+
+ if (!(fgets(line, sizeof(line)-1, status_file))) {
+ pa_log_warn("Failed to read from %s: %s", sub_status, pa_cstrerror(errno));
+ continue;
+ }
+
+ if (!pa_streq(line, "closed\n")) {
+ busy = TRUE;
+ break;
+ }
+ }
+ }
+
+fail:
+
+ pa_xfree(card_path);
+ pa_xfree(pcm_path);
+ pa_xfree(sub_status);
+ pa_xfree(space);
+
+ if (card_dir)
+ closedir(card_dir);
+
+ if (pcm_dir)
+ closedir(pcm_dir);
+
+ if (status_file)
+ fclose(status_file);
+
+ return busy;
+}
+
static void verify_access(struct userdata *u, struct device *d) {
char *cd;
pa_card *card;
+ pa_bool_t accessible;
pa_assert(u);
pa_assert(d);
cd = pa_sprintf_malloc("%s/snd/controlC%s", udev_get_dev_path(u->udev), path_get_card_id(d->path));
- d->accessible = access(cd, R_OK|W_OK) >= 0;
+ accessible = access(cd, R_OK|W_OK) >= 0;
+ pa_log_debug("%s is accessible: %s", cd, pa_yes_no(accessible));
- pa_log_info("%s is accessible: %s", cd, pa_yes_no(d->accessible));
pa_xfree(cd);
if (d->module == PA_INVALID_INDEX) {
- /* If we not loaded, try to load */
+ /* If we are not loaded, try to load */
- if (d->accessible) {
+ if (accessible) {
pa_module *m;
-
- pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args);
- m = pa_module_load(u->core, "module-alsa-card", d->args);
-
- if (m) {
- d->module = m->index;
- pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name);
- } else
- pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name);
+ pa_bool_t busy;
+
+ /* Check if any of the PCM devices that belong to this
+ * card are currently busy. If they are, don't try to load
+ * right now, to make sure the probing phase can
+ * successfully complete. When the current user of the
+ * device closes it we will get another notification via
+ * inotify and can then recheck. */
+
+ busy = is_card_busy(path_get_card_id(d->path));
+ pa_log_debug("%s is busy: %s", d->path, pa_yes_no(busy));
+
+ if (!busy) {
+ pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args);
+ m = pa_module_load(u->core, "module-alsa-card", d->args);
+
+ if (m) {
+ d->module = m->index;
+ pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name);
+ } else
+ pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name);
+ }
}
} else {
@@ -135,7 +251,7 @@ static void verify_access(struct userdata *u, struct device *d) {
* accessible boolean */
if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD)))
- pa_card_suspend(card, !d->accessible, PA_SUSPEND_SESSION);
+ pa_card_suspend(card, !accessible, PA_SUSPEND_SESSION);
}
}
@@ -160,7 +276,6 @@ static void card_changed(struct userdata *u, struct udev_device *dev) {
d = pa_xnew0(struct device, 1);
d->path = pa_xstrdup(path);
- d->accessible = TRUE;
d->module = PA_INVALID_INDEX;
if (!(t = udev_device_get_property_value(dev, "PULSE_NAME")))