static void card_changed(struct userdata *u, struct udev_device *dev) { struct device *d; const char *path; const char *t; char *n; pa_assert(u); pa_assert(dev); /* Maybe /dev/snd is now available? */ setup_inotify(u); path = udev_device_get_devpath(dev); if ((d = pa_hashmap_get(u->devices, path))) { verify_access(u, d); return; } d = pa_xnew0(struct device, 1); d->path = pa_xstrdup(path); d->module = PA_INVALID_INDEX; PA_INIT_RATELIMIT(d->ratelimit, 10*PA_USEC_PER_SEC, 5); if (!(t = udev_device_get_property_value(dev, "PULSE_NAME"))) if (!(t = udev_device_get_property_value(dev, "ID_ID"))) if (!(t = udev_device_get_property_value(dev, "ID_PATH"))) t = path_get_card_id(path); n = pa_namereg_make_valid_name(t); d->card_name = pa_sprintf_malloc("alsa_card.%s", n); d->args = pa_sprintf_malloc("device_id=\"%s\" " "name=\"%s\" " "card_name=\"%s\" " "namereg_fail=false " "tsched=%s " "fixed_latency_range=%s " "ignore_dB=%s " "deferred_volume=%s " "use_ucm=1 " "card_properties=\"module-udev-detect.discovered=1\"", path_get_card_id(path), n, d->card_name, pa_yes_no(u->use_tsched), pa_yes_no(u->fixed_latency_range), pa_yes_no(u->ignore_dB), pa_yes_no(u->deferred_volume)); pa_xfree(n); pa_hashmap_put(u->devices, d->path, d); verify_access(u, d); }
static void monitor_cb( pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { struct userdata *u = userdata; struct udev_device *dev; pa_assert(a); if (!(dev = udev_monitor_receive_device(u->monitor))) { pa_log("Failed to get udev device object from monitor."); goto fail; } if (!path_get_card_id(udev_device_get_devpath(dev))) { udev_device_unref(dev); return; } process_device(u, dev); udev_device_unref(dev); return; fail: a->io_free(u->udev_io); u->udev_io = NULL; }
static bool control_node_belongs_to_device( struct device *d, const char *node) { char *cd; bool b; cd = pa_sprintf_malloc("controlC%s", path_get_card_id(d->path)); b = pa_streq(node, cd); pa_xfree(cd); return b; }
static bool pcm_node_belongs_to_device( struct device *d, const char *node) { char *cd; bool b; cd = pa_sprintf_malloc("pcmC%sD", path_get_card_id(d->path)); b = pa_startswith(node, cd); pa_xfree(cd); return b; }
static void process_path(struct userdata *u, const char *path) { struct udev_device *dev; if (!path_get_card_id(path)) return; if (!(dev = udev_device_new_from_syspath(u->udev, path))) { pa_log("Failed to get udev device object from udev."); return; } process_device(u, dev); udev_device_unref(dev); }
static void verify_access(struct userdata *u, struct device *d) { char *cd; pa_card *card; bool accessible; pa_assert(u); pa_assert(d); cd = pa_sprintf_malloc("/dev/snd/controlC%s", path_get_card_id(d->path)); accessible = access(cd, R_OK|W_OK) >= 0; pa_log_debug("%s is accessible: %s", cd, pa_yes_no(accessible)); pa_xfree(cd); if (d->module == PA_INVALID_INDEX) { /* If we are not loaded, try to load */ if (accessible) { pa_module *m; bool 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) { /* So, why do we rate limit here? It's certainly ugly, * but there seems to be no other way. Problem is * this: if we are unable to configure/probe an audio * device after opening it we will close it again and * the module initialization will fail. This will then * cause an inotify event on the device node which * will be forwarded to us. We then try to reopen the * audio device again, practically entering a busy * loop. * * A clean fix would be if we would be able to ignore * our own inotify close events. However, inotify * lacks such functionality. Also, during probing of * the device we cannot really distinguish between * other processes causing EBUSY or ourselves, which * means we have no way to figure out if the probing * during opening was canceled by a "try again" * failure or a "fatal" failure. */ if (pa_ratelimit_test(&d->ratelimit, PA_LOG_DEBUG)) { 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 pa_log_warn("Tried to configure %s (%s) more often than %u times in %llus", d->path, d->card_name, d->ratelimit.burst, (long long unsigned) (d->ratelimit.interval / PA_USEC_PER_SEC)); } } } else { /* If we are already loaded update suspend status with * accessible boolean */ if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) { pa_log_debug("%s all sinks and sources of card %s.", accessible ? "Resuming" : "Suspending", d->card_name); pa_card_suspend(card, !accessible, PA_SUSPEND_SESSION); } } }