From 4d874753f5ba838b015c69f281e991c7a42381eb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Jun 2009 23:19:48 +0200 Subject: utils: use pa_path_get_filename() where applicable --- src/daemon/cmdline.c | 11 +++-------- src/utils/pactl.c | 14 ++------------ src/utils/pasuspender.c | 5 +---- 3 files changed, 6 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c index d78089e1..ecb38486 100644 --- a/src/daemon/cmdline.c +++ b/src/daemon/cmdline.c @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -109,15 +110,8 @@ static const struct option long_options[] = { }; void pa_cmdline_help(const char *argv0) { - const char *e; - pa_assert(argv0); - if ((e = strrchr(argv0, '/'))) - e++; - else - e = argv0; - printf(_("%s [options]\n\n" "COMMANDS:\n" " -h, --help Show this help\n" @@ -172,7 +166,8 @@ void pa_cmdline_help(const char *argv0) { " -C Open a command line on the running TTY\n" " after startup\n\n" - " -n Don't load default script file\n"), e); + " -n Don't load default script file\n"), + pa_path_get_filename(argv0)); } int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) { diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 53c67666..6608c01e 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -802,7 +802,6 @@ enum { int main(int argc, char *argv[]) { pa_mainloop* m = NULL; - char tmp[PATH_MAX]; int ret = 1, c; char *server = NULL, *bn; @@ -882,17 +881,8 @@ int main(int argc, char *argv[]) { if (optind+2 < argc) sample_name = pa_xstrdup(argv[optind+2]); else { - char *f = strrchr(argv[optind+1], '/'); - size_t n; - if (f) - f++; - else - f = argv[optind]; - - n = strcspn(f, "."); - strncpy(tmp, f, n); - tmp[n] = 0; - sample_name = pa_xstrdup(tmp); + char *f = pa_path_get_filename(argv[optind+1]); + sample_name = pa_xstrndup(f, strcspn(f, ".")); } pa_zero(sfi); diff --git a/src/utils/pasuspender.c b/src/utils/pasuspender.c index b4bccd56..c327ee41 100644 --- a/src/utils/pasuspender.c +++ b/src/utils/pasuspender.c @@ -235,10 +235,7 @@ int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR); - if (!(bn = strrchr(argv[0], '/'))) - bn = argv[0]; - else - bn++; + bn = pa_path_get_filename(argv[0]); while ((c = getopt_long(argc, argv, "s:h", long_options, NULL)) != -1) { switch (c) { -- cgit From 3e10f3f4e3bde6bb772eab70a1008895a302494f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Jun 2009 23:50:43 +0200 Subject: tdb: include signal.h before tdb.h for compat reasons --- src/pulsecore/database-tdb.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/pulsecore/database-tdb.c b/src/pulsecore/database-tdb.c index c35fd81f..b79d2837 100644 --- a/src/pulsecore/database-tdb.c +++ b/src/pulsecore/database-tdb.c @@ -26,6 +26,9 @@ #include #include #include + +/* Some versions of tdb lack inclusion of signal.h in the header files but use sigatomic_t */ +#include #include #include -- cgit From 1748fd2a0d327201ee57847ba3d9a8209f8d98d6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 5 Jun 2009 19:00:12 +0200 Subject: reserve: update reserve.[ch] from upstream git --- src/modules/reserve.c | 22 ++++++++-------------- src/modules/reserve.h | 2 +- 2 files changed, 9 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/modules/reserve.c b/src/modules/reserve.c index 9a9591d2..09bc46cb 100644 --- a/src/modules/reserve.c +++ b/src/modules/reserve.c @@ -43,16 +43,15 @@ struct rd_device { DBusConnection *connection; - int owning:1; - int registered:1; - int filtering:1; - int gave_up:1; + unsigned owning:1; + unsigned registered:1; + unsigned filtering:1; + unsigned gave_up:1; rd_request_cb_t request_cb; void *userdata; }; - #define SERVICE_PREFIX "org.freedesktop.ReserveDevice1." #define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/" @@ -297,6 +296,7 @@ static DBusHandlerResult filter_handler( dbus_error_init(&error); d = userdata; + assert(d->ref >= 1); if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameLost")) { const char *name; @@ -560,7 +560,7 @@ void rd_release( assert(d->ref > 0); - if (--d->ref) + if (--d->ref > 0) return; @@ -575,17 +575,11 @@ void rd_release( d->connection, d->object_path); - if (d->owning) { - DBusError error; - dbus_error_init(&error); - + if (d->owning) dbus_bus_release_name( d->connection, d->service_name, - &error); - - dbus_error_free(&error); - } + NULL); free(d->device_name); free(d->application_name); diff --git a/src/modules/reserve.h b/src/modules/reserve.h index b315a08c..31071298 100644 --- a/src/modules/reserve.h +++ b/src/modules/reserve.h @@ -45,7 +45,7 @@ typedef int (*rd_request_cb_t)( * the error was caused D-Bus. */ int rd_acquire( rd_device **d, /* On success a pointer to the newly allocated rd_device object will be filled in here */ - DBusConnection *connection, + DBusConnection *connection, /* Session bus (when D-Bus learns about user busses we should switchg to user busses) */ const char *device_name, /* The device to lock, e.g. "Audio0" */ const char *application_name, /* A human readable name of the application, e.g. "PulseAudio Sound Server" */ int32_t priority, /* The priority for this application. If unsure use 0 */ -- cgit From 3af5f8cb553e512305c1f3603b57424c36c9c82c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 5 Jun 2009 19:03:16 +0200 Subject: reserve: wrap device reservation monitor reference implementation --- src/Makefile.am | 4 +- src/modules/reserve-monitor.c | 259 ++++++++++++++++++++++++++++++++++++++++++ src/modules/reserve-monitor.h | 62 ++++++++++ src/modules/reserve-wrap.c | 149 +++++++++++++++++++++++- src/modules/reserve-wrap.h | 9 +- 5 files changed, 479 insertions(+), 4 deletions(-) create mode 100644 src/modules/reserve-monitor.c create mode 100644 src/modules/reserve-monitor.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index a7ec6917..c0e1806f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1358,7 +1358,7 @@ libalsa_util_la_CFLAGS += $(UDEV_CFLAGS) endif if HAVE_DBUS -libalsa_util_la_SOURCES += modules/reserve.h modules/reserve.c +libalsa_util_la_SOURCES += modules/reserve.h modules/reserve.c modules/reserve-monitor.h modules/reserve-monitor.c libalsa_util_la_LIBADD += $(DBUS_LIBS) libalsa_util_la_CFLAGS += $(DBUS_CFLAGS) endif @@ -1670,7 +1670,7 @@ update-sbc: done update-reserve: - for i in reserve.c reserve.h ; do \ + for i in reserve.c reserve.h reserve-monitor.c reserve-monitor.h ; do \ wget -O modules/$$i http://git.0pointer.de/\?p=reserve.git\;a=blob_plain\;f=$$i\;hb=master ; \ done diff --git a/src/modules/reserve-monitor.c b/src/modules/reserve-monitor.c new file mode 100644 index 00000000..64d2a7cc --- /dev/null +++ b/src/modules/reserve-monitor.c @@ -0,0 +1,259 @@ +/*** + Copyright 2009 Lennart Poettering + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include +#include +#include +#include +#include +#include + +#include "reserve-monitor.h" + +struct rm_monitor { + int ref; + + char *device_name; + char *service_name; + + DBusConnection *connection; + + unsigned busy:1; + unsigned filtering:1; + unsigned matching:1; + + rm_change_cb_t change_cb; + void *userdata; +}; + +#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1." + +static DBusHandlerResult filter_handler( + DBusConnection *c, + DBusMessage *s, + void *userdata) { + + DBusMessage *reply; + rm_monitor *m; + DBusError error; + + dbus_error_init(&error); + + m = userdata; + assert(m->ref >= 1); + + if (dbus_message_is_signal(s, "org.freedesktop.DBus", "NameOwnerChanged")) { + const char *name, *old, *new; + + if (!dbus_message_get_args( + s, + &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) + goto invalid; + + if (strcmp(name, m->service_name) == 0) { + + m->busy = !!(new && *new); + + if (m->change_cb) { + m->ref++; + m->change_cb(m); + rm_release(m); + } + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +invalid: + if (!(reply = dbus_message_new_error( + s, + DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"))) + goto oom; + + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +int rm_watch( + rm_monitor **_m, + DBusConnection *connection, + const char*device_name, + rm_change_cb_t change_cb, + DBusError *error) { + + rm_monitor *m = NULL; + int r; + DBusError _error; + + if (!error) + error = &_error; + + dbus_error_init(error); + + if (!_m) + return -EINVAL; + + if (!connection) + return -EINVAL; + + if (!device_name) + return -EINVAL; + + if (!(m = calloc(sizeof(rm_monitor), 1))) + return -ENOMEM; + + m->ref = 1; + + if (!(m->device_name = strdup(device_name))) { + r = -ENOMEM; + goto fail; + } + + m->connection = dbus_connection_ref(connection); + m->change_cb = change_cb; + + if (!(m->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) { + r = -ENOMEM; + goto fail; + } + sprintf(m->service_name, SERVICE_PREFIX "%s", m->device_name); + + if (!(dbus_connection_add_filter(m->connection, filter_handler, m, NULL))) { + r = -ENOMEM; + goto fail; + } + + m->filtering = 1; + + dbus_bus_add_match(m->connection, + "type='signal'," + "sender='" DBUS_SERVICE_DBUS "'," + "interface='" DBUS_INTERFACE_DBUS "'," + "member='NameOwnerChanged'", error); + + if (dbus_error_is_set(error)) { + r = -EIO; + goto fail; + } + + m->matching = 1; + + m->busy = dbus_bus_name_has_owner(m->connection, m->service_name, error); + + if (dbus_error_is_set(error)) { + r = -EIO; + goto fail; + } + + *_m = m; + return 0; + +fail: + if (&_error == error) + dbus_error_free(&_error); + + if (m) + rm_release(m); + + return r; +} + +void rm_release(rm_monitor *m) { + if (!m) + return; + + assert(m->ref > 0); + + if (--m->ref > 0) + return; + + if (m->matching) + dbus_bus_remove_match( + m->connection, + "type='signal'," + "sender='" DBUS_SERVICE_DBUS "'," + "interface='" DBUS_INTERFACE_DBUS "'," + "member='NameOwnerChanged'", NULL); + + if (m->filtering) + dbus_connection_remove_filter( + m->connection, + filter_handler, + m); + + free(m->device_name); + free(m->service_name); + + if (m->connection) + dbus_connection_unref(m->connection); + + free(m); +} + +int rm_busy(rm_monitor *m) { + if (!m) + return -EINVAL; + + assert(m->ref > 0); + + return m->busy; +} + +void rm_set_userdata(rm_monitor *m, void *userdata) { + + if (!m) + return; + + assert(m->ref > 0); + m->userdata = userdata; +} + +void* rm_get_userdata(rm_monitor *m) { + + if (!m) + return NULL; + + assert(m->ref > 0); + + return m->userdata; +} diff --git a/src/modules/reserve-monitor.h b/src/modules/reserve-monitor.h new file mode 100644 index 00000000..4f4a8332 --- /dev/null +++ b/src/modules/reserve-monitor.h @@ -0,0 +1,62 @@ +#ifndef fooreservemonitorhfoo +#define fooreservemonitorhfoo + +/*** + Copyright 2009 Lennart Poettering + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include +#include + +typedef struct rm_monitor rm_monitor; + +/* Prototype for a function that is called whenever the reservation + * device of a device changes. Use rm_monitor_busy() to find out the + * new state.*/ +typedef void (*rm_change_cb_t)(rm_monitor *m); + +/* Creates a monitor for watching the lock status of a device. Returns + * 0 on success, a negative errno style return value on error. The + * DBus error might be set as well if the error was caused D-Bus. */ +int rm_watch( + rm_monitor **m, /* On success a pointer to the newly allocated rm_device object will be filled in here */ + DBusConnection *connection, /* Session bus (when D-Bus learns about user busses we should switchg to user busses) */ + const char *device_name, /* The device to monitor, e.g. "Audio0" */ + rm_change_cb_t change_cb, /* Will be called whenever the lock status changes. May be NULL */ + DBusError *error); /* If we fail due to a D-Bus related issue the error will be filled in here. May be NULL. */ + +/* Free a rm_monitor object */ +void rm_release(rm_monitor *m); + +/* Checks whether the device is currently reserved, and returns 1 + * then, 0 if not, negative errno style error code value on error. */ +int rm_busy(rm_monitor *m); + +/* Attach a userdata pointer to an rm_monitor */ +void rm_set_userdata(rm_monitor *m, void *userdata); + +/* Query the userdata pointer from an rm_monitor. Returns NULL if no + * userdata was set. */ +void* rm_get_userdata(rm_monitor *m); + +#endif diff --git a/src/modules/reserve-wrap.c b/src/modules/reserve-wrap.c index d0d014d3..07b592d3 100644 --- a/src/modules/reserve-wrap.c +++ b/src/modules/reserve-wrap.c @@ -35,6 +35,7 @@ #ifdef HAVE_DBUS #include #include "reserve.h" +#include "reserve-monitor.h" #endif #include "reserve-wrap.h" @@ -50,6 +51,17 @@ struct pa_reserve_wrapper { #endif }; +struct pa_reserve_monitor_wrapper { + PA_REFCNT_DECLARE; + pa_core *core; + pa_hook hook; + char *shared_name; +#ifdef HAVE_DBUS + pa_dbus_connection *connection; + struct rm_monitor *monitor; +#endif +}; + static void reserve_wrapper_free(pa_reserve_wrapper *r) { pa_assert(r); @@ -83,7 +95,7 @@ static int request_cb(rd_device *d, int forced) { PA_REFCNT_INC(r); k = pa_hook_fire(&r->hook, PA_INT_TO_PTR(forced)); - pa_log_debug("Device unlock has been requested and %s.", k < 0 ? "failed" : "succeeded"); + pa_log_debug("Device unlock of %s has been requested and %s.", r->shared_name, k < 0 ? "failed" : "succeeded"); pa_reserve_wrapper_unref(r); @@ -191,3 +203,138 @@ void pa_reserve_wrapper_set_application_device_name(pa_reserve_wrapper *r, const rd_set_application_device_name(r->device, name); #endif } + +static void reserve_monitor_wrapper_free(pa_reserve_monitor_wrapper *w) { + pa_assert(w); + +#ifdef HAVE_DBUS + if (w->monitor) + rm_release(w->monitor); + + if (w->connection) + pa_dbus_connection_unref(w->connection); +#endif + + pa_hook_done(&w->hook); + + if (w->shared_name) { + pa_assert_se(pa_shared_remove(w->core, w->shared_name) >= 0); + pa_xfree(w->shared_name); + } + + pa_xfree(w); +} + +#ifdef HAVE_DBUS +static void change_cb(rm_monitor *m) { + pa_reserve_monitor_wrapper *w; + int k; + + pa_assert(m); + pa_assert_se(w = rm_get_userdata(m)); + pa_assert(PA_REFCNT_VALUE(w) >= 1); + + PA_REFCNT_INC(w); + + if ((k = rm_busy(w->monitor)) < 0) + return; + + pa_hook_fire(&w->hook, PA_INT_TO_PTR(!!k)); + pa_log_debug("Device lock status of %s changed: %s", w->shared_name, k ? "busy" : "not busy"); + + pa_reserve_monitor_wrapper_unref(w); +} +#endif + +pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const char *device_name) { + pa_reserve_monitor_wrapper *w; + int k; + char *t; +#ifdef HAVE_DBUS + DBusError error; + + dbus_error_init(&error); +#endif + + pa_assert(c); + pa_assert(device_name); + + t = pa_sprintf_malloc("reserve-monitor-wrapper@%s", device_name); + + if ((w = pa_shared_get(c, t))) { + pa_xfree(t); + + pa_assert(PA_REFCNT_VALUE(w) >= 1); + PA_REFCNT_INC(w); + + return w; + } + + w = pa_xnew0(pa_reserve_monitor_wrapper, 1); + PA_REFCNT_INIT(w); + w->core = c; + pa_hook_init(&w->hook, w); + w->shared_name = t; + + pa_assert_se(pa_shared_set(c, w->shared_name, w) >= 0); + +#ifdef HAVE_DBUS + if (!(w->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { + pa_log_warn("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); + + /* We don't treat this as error here because we want allow PA + * to run even when no session bus is available. */ + return w; + } + + if ((k = rm_watch( + &w->monitor, + pa_dbus_connection_get(w->connection), + device_name, + change_cb, + NULL)) < 0) { + + pa_log_warn("Failed to create watch on device '%s': %s", device_name, pa_cstrerror(-k)); + goto fail; + } + + pa_log_debug("Successfully create reservation lock monitor for device '%s'", device_name); + + rm_set_userdata(w->monitor, w); + return w; + +fail: + dbus_error_free(&error); + + reserve_monitor_wrapper_free(w); + + return NULL; +#else + return w; +#endif +} + +void pa_reserve_monitor_wrapper_unref(pa_reserve_monitor_wrapper *w) { + pa_assert(w); + pa_assert(PA_REFCNT_VALUE(w) >= 1); + + if (PA_REFCNT_DEC(w) > 0) + return; + + reserve_monitor_wrapper_free(w); +} + +pa_hook* pa_reserve_monitor_wrapper_hook(pa_reserve_monitor_wrapper *w) { + pa_assert(w); + pa_assert(PA_REFCNT_VALUE(w) >= 1); + + return &w->hook; +} + +pa_bool_t pa_reserve_monitor_wrapper_busy(pa_reserve_monitor_wrapper *w) { + pa_assert(w); + + pa_assert(PA_REFCNT_VALUE(w) >= 1); + + return rm_busy(w->monitor) > 0; +} diff --git a/src/modules/reserve-wrap.h b/src/modules/reserve-wrap.h index 2b97c91c..2de6c093 100644 --- a/src/modules/reserve-wrap.h +++ b/src/modules/reserve-wrap.h @@ -28,11 +28,18 @@ typedef struct pa_reserve_wrapper pa_reserve_wrapper; pa_reserve_wrapper* pa_reserve_wrapper_get(pa_core *c, const char *device_name); - void pa_reserve_wrapper_unref(pa_reserve_wrapper *r); pa_hook* pa_reserve_wrapper_hook(pa_reserve_wrapper *r); void pa_reserve_wrapper_set_application_device_name(pa_reserve_wrapper *r, const char *name); +typedef struct pa_reserve_monitor_wrapper pa_reserve_monitor_wrapper; + +pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const char *device_name); +void pa_reserve_monitor_wrapper_unref(pa_reserve_monitor_wrapper *m); + +pa_hook* pa_reserve_monitor_wrapper_hook(pa_reserve_monitor_wrapper *m); +pa_bool_t pa_reserve_monitor_wrapper_busy(pa_reserve_monitor_wrapper *m); + #endif -- cgit From 00797b8b6ea7978f862facb7181fb04895caf23c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 5 Jun 2009 19:05:07 +0200 Subject: core: add a suspend cause flags field --- src/modules/alsa/alsa-sink.c | 2 +- src/modules/alsa/alsa-source.c | 2 +- src/modules/module-combine.c | 4 ++-- src/modules/module-hal-detect.c | 12 ++++++------ src/modules/module-suspend-on-idle.c | 12 ++++++------ src/pulsecore/card.c | 7 ++++--- src/pulsecore/card.h | 2 +- src/pulsecore/cli-command.c | 8 ++++---- src/pulsecore/cli-text.c | 10 ++++++++++ src/pulsecore/core.h | 10 ++++++++++ src/pulsecore/protocol-esound.c | 4 ++-- src/pulsecore/protocol-native.c | 8 ++++---- src/pulsecore/sink.c | 16 +++++++++++++--- src/pulsecore/sink.h | 5 +++-- src/pulsecore/source.c | 16 +++++++++++++--- src/pulsecore/source.h | 5 +++-- 16 files changed, 83 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 59f53110..b1adc528 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -124,7 +124,7 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u pa_assert(r); pa_assert(u); - if (pa_sink_suspend(u->sink, TRUE) < 0) + if (pa_sink_suspend(u->sink, TRUE, PA_SUSPEND_APPLICATION) < 0) return PA_HOOK_CANCEL; return PA_HOOK_OK; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index f1c18196..68f697d2 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -122,7 +122,7 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u pa_assert(r); pa_assert(u); - if (pa_source_suspend(u->source, TRUE) < 0) + if (pa_source_suspend(u->source, TRUE, PA_SUSPEND_APPLICATION) < 0) return PA_HOOK_CANCEL; return PA_HOOK_OK; diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 02a7e1ff..725faa0c 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -593,7 +593,7 @@ static void unsuspend(struct userdata *u) { /* Let's resume */ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) { - pa_sink_suspend(o->sink, FALSE); + pa_sink_suspend(o->sink, FALSE, PA_SUSPEND_IDLE); if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) enable_output(o); @@ -873,7 +873,7 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) { } if (PA_SINK_IS_OPENED(state) || state == PA_SINK_INIT) { - pa_sink_suspend(sink, FALSE); + pa_sink_suspend(sink, FALSE, PA_SUSPEND_IDLE); if (PA_SINK_IS_OPENED(pa_sink_get_state(sink))) if (output_create_sink_input(o) < 0) diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index b6139e43..9ac87050 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -567,7 +567,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo pa_sink *sink; if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) { - pa_bool_t success = pa_sink_suspend(sink, suspend) >= 0; + pa_bool_t success = pa_sink_suspend(sink, suspend, PA_SUSPEND_SESSION) >= 0; if (!success && !suspend) d->acl_race_fix = TRUE; /* resume failed, let's try again */ @@ -580,7 +580,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo pa_source *source; if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) { - pa_bool_t success = pa_source_suspend(source, suspend) >= 0; + pa_bool_t success = pa_source_suspend(source, suspend, PA_SUSPEND_SESSION) >= 0; if (!success && !suspend) d->acl_race_fix = TRUE; /* resume failed, let's try again */ @@ -593,7 +593,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo pa_card *card; if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) { - pa_bool_t success = pa_card_suspend(card, suspend) >= 0; + pa_bool_t success = pa_card_suspend(card, suspend, PA_SUSPEND_SESSION) >= 0; if (!success && !suspend) d->acl_race_fix = TRUE; /* resume failed, let's try again */ @@ -637,21 +637,21 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo pa_sink *sink; if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) - pa_sink_suspend(sink, FALSE); + pa_sink_suspend(sink, FALSE, PA_SUSPEND_SESSION); } if (d->source_name) { pa_source *source; if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) - pa_source_suspend(source, FALSE); + pa_source_suspend(source, FALSE, PA_SUSPEND_SESSION); } if (d->card_name) { pa_card *card; if ((card = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_CARD))) - pa_card_suspend(card, FALSE); + pa_card_suspend(card, FALSE, PA_SUSPEND_SESSION); } } diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index cc69d74c..c5b78911 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -86,14 +86,14 @@ static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval d->userdata->core->mainloop->time_restart(d->time_event, NULL); - if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) { + if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && !(d->sink->suspend_cause & PA_SUSPEND_IDLE)) { pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name); - pa_sink_suspend(d->sink, TRUE); + pa_sink_suspend(d->sink, TRUE, PA_SUSPEND_IDLE); } - if (d->source && pa_source_check_suspend(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) { + if (d->source && pa_source_check_suspend(d->source) <= 0 && !(d->source->suspend_cause & PA_SUSPEND_IDLE)) { pa_log_info("Source %s idle for too long, suspending ...", d->source->name); - pa_source_suspend(d->source, TRUE); + pa_source_suspend(d->source, TRUE, PA_SUSPEND_IDLE); } } @@ -127,13 +127,13 @@ static void resume(struct device_info *d) { d->userdata->core->mainloop->time_restart(d->time_event, NULL); if (d->sink) { - pa_sink_suspend(d->sink, FALSE); + pa_sink_suspend(d->sink, FALSE, PA_SUSPEND_IDLE); pa_log_debug("Sink %s becomes busy.", d->sink->name); } if (d->source) { - pa_source_suspend(d->source, FALSE); + pa_source_suspend(d->source, FALSE, PA_SUSPEND_IDLE); pa_log_debug("Source %s becomes busy.", d->source->name); } diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c index 8101a92e..59b8cda6 100644 --- a/src/pulsecore/card.c +++ b/src/pulsecore/card.c @@ -244,19 +244,20 @@ int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save) { return 0; } -int pa_card_suspend(pa_card *c, pa_bool_t suspend) { +int pa_card_suspend(pa_card *c, pa_bool_t suspend, pa_suspend_cause_t cause) { pa_sink *sink; pa_source *source; uint32_t idx; int ret = 0; pa_assert(c); + pa_assert(cause != 0); for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) - ret -= pa_sink_suspend(sink, suspend) < 0; + ret -= pa_sink_suspend(sink, suspend, cause) < 0; for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) - ret -= pa_source_suspend(source, suspend) < 0; + ret -= pa_source_suspend(source, suspend, cause) < 0; return ret; } diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h index 3b7608f6..415ab678 100644 --- a/src/pulsecore/card.h +++ b/src/pulsecore/card.h @@ -99,6 +99,6 @@ void pa_card_free(pa_card *c); int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save); -int pa_card_suspend(pa_card *c, pa_bool_t suspend); +int pa_card_suspend(pa_card *c, pa_bool_t suspend, pa_suspend_cause_t cause); #endif diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index dad647a8..644de96e 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -1278,7 +1278,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b return -1; } - if ((r = pa_sink_suspend(sink, suspend)) < 0) + if ((r = pa_sink_suspend(sink, suspend, PA_SUSPEND_USER)) < 0) pa_strbuf_printf(buf, "Failed to resume/suspend sink: %s\n", pa_strerror(r)); return 0; @@ -1314,7 +1314,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf return -1; } - if ((r = pa_source_suspend(source, suspend)) < 0) + if ((r = pa_source_suspend(source, suspend, PA_SUSPEND_USER)) < 0) pa_strbuf_printf(buf, "Failed to resume/suspend source: %s\n", pa_strerror(r)); return 0; @@ -1339,10 +1339,10 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p return -1; } - if ((r = pa_sink_suspend_all(c, suspend)) < 0) + if ((r = pa_sink_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0) pa_strbuf_printf(buf, "Failed to resume/suspend all sinks: %s\n", pa_strerror(r)); - if ((r = pa_source_suspend_all(c, suspend)) < 0) + if ((r = pa_source_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0) pa_strbuf_printf(buf, "Failed to resume/suspend all sources: %s\n", pa_strerror(r)); return 0; diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 604678be..bc863f05 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -232,6 +232,7 @@ char *pa_sink_list_to_string(pa_core *c) { "\tdriver: <%s>\n" "\tflags: %s%s%s%s%s%s%s%s\n" "\tstate: %s\n" + "\tsuspend cause: %s%s%s%s\n" "\tvolume: %s%s%s\n" "\t balance %0.2f\n" "\tbase volume: %s%s%s\n" @@ -258,6 +259,10 @@ char *pa_sink_list_to_string(pa_core *c) { sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME " : "", sink->flags & PA_SINK_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "", sink_state_to_string(pa_sink_get_state(sink)), + sink->suspend_cause & PA_SUSPEND_USER ? "USER " : "", + sink->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "", + sink->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "", + sink->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "", pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE, FALSE)), sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "", sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE, FALSE)) : "", @@ -335,6 +340,7 @@ char *pa_source_list_to_string(pa_core *c) { "\tdriver: <%s>\n" "\tflags: %s%s%s%s%s%s%s\n" "\tstate: %s\n" + "\tsuspend cause: %s%s%s%s\n" "\tvolume: %s%s%s\n" "\t balance %0.2f\n" "\tbase volume: %s%s%s\n" @@ -358,6 +364,10 @@ char *pa_source_list_to_string(pa_core *c) { source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "", source->flags & PA_SOURCE_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "", source_state_to_string(pa_source_get_state(source)), + source->suspend_cause & PA_SUSPEND_USER ? "USER " : "", + source->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "", + source->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "", + source->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "", pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)), source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "", source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_source_get_volume(source, FALSE)) : "", diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index c6794445..09a880c4 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -27,6 +27,16 @@ typedef struct pa_core pa_core; +/* This is a bitmask that encodes the cause why a sink/source is + * suspended. */ +typedef enum pa_suspend_cause { + PA_SUSPEND_USER = 1, /* Exposed to the user via some protocol */ + PA_SUSPEND_APPLICATION = 2, /* Used by the device reservation logic */ + PA_SUSPEND_IDLE = 4, /* Used by module-suspend-on-idle */ + PA_SUSPEND_SESSION = 8, /* Used by module-hal for mark inactive sessions */ + PA_SUSPEND_ALL = 0xFFFF /* Magic cause that can be used to resume forcibly */ +} pa_suspend_cause_t; + #include #include #include diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 7e7126ea..ad7cd045 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -947,10 +947,10 @@ static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const connection_write(c, &ok, sizeof(int32_t)); if (request == ESD_PROTO_STANDBY) - ok = pa_sink_suspend_all(c->protocol->core, TRUE) >= 0; + ok = pa_sink_suspend_all(c->protocol->core, TRUE, PA_SUSPEND_USER) >= 0; else { pa_assert(request == ESD_PROTO_RESUME); - ok = pa_sink_suspend_all(c->protocol->core, FALSE) >= 0; + ok = pa_sink_suspend_all(c->protocol->core, FALSE, PA_SUSPEND_USER) >= 0; } connection_write(c, &ok, sizeof(int32_t)); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index d4a9952a..e9e2d601 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -4098,7 +4098,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa pa_log_debug("%s all sinks", b ? "Suspending" : "Resuming"); - if (pa_sink_suspend_all(c->protocol->core, b) < 0) { + if (pa_sink_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) { pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); return; } @@ -4112,7 +4112,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY); - if (pa_sink_suspend(sink, b) < 0) { + if (pa_sink_suspend(sink, b, PA_SUSPEND_USER) < 0) { pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); return; } @@ -4125,7 +4125,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa pa_log_debug("%s all sources", b ? "Suspending" : "Resuming"); - if (pa_source_suspend_all(c->protocol->core, b) < 0) { + if (pa_source_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) { pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); return; } @@ -4140,7 +4140,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY); - if (pa_source_suspend(source, b) < 0) { + if (pa_source_suspend(source, b, PA_SUSPEND_USER) < 0) { pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); return; } diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 28b3440c..3c4adc6c 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -190,6 +190,7 @@ pa_sink* pa_sink_new( s->core = core; s->state = PA_SINK_INIT; s->flags = flags; + s->suspend_cause = 0; s->name = pa_xstrdup(name); s->proplist = pa_proplist_copy(data->proplist); s->driver = pa_xstrdup(pa_path_get_filename(data->driver)); @@ -499,11 +500,19 @@ int pa_sink_update_status(pa_sink*s) { } /* Called from main context */ -int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) { +int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) { pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); + pa_assert(cause != 0); if (suspend) + s->suspend_cause |= cause; + else + s->suspend_cause &= ~cause; + + pa_log_debug("Suspend cause of sink %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming"); + + if (s->suspend_cause) return sink_set_state(s, PA_SINK_SUSPENDED); else return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE); @@ -1823,17 +1832,18 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse } /* Called from main thread */ -int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) { +int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) { pa_sink *sink; uint32_t idx; int ret = 0; pa_core_assert_ref(c); + pa_assert(cause != 0); for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx))) { int r; - if ((r = pa_sink_suspend(sink, suspend)) < 0) + if ((r = pa_sink_suspend(sink, suspend, cause)) < 0) ret = r; } diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index e33b3cfc..4dce3f93 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -56,6 +56,7 @@ struct pa_sink { pa_core *core; pa_sink_state_t state; pa_sink_flags_t flags; + pa_suspend_cause_t suspend_cause; char *name; char *driver; /* may be NULL */ @@ -252,8 +253,8 @@ size_t pa_sink_get_max_rewind(pa_sink *s); size_t pa_sink_get_max_request(pa_sink *s); int pa_sink_update_status(pa_sink*s); -int pa_sink_suspend(pa_sink *s, pa_bool_t suspend); -int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend); +int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause); +int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause); void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume); void pa_sink_propagate_flat_volume(pa_sink *s); diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 8aeb5606..4ade18f6 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -180,6 +180,7 @@ pa_source* pa_source_new( s->core = core; s->state = PA_SOURCE_INIT; s->flags = flags; + s->suspend_cause = 0; s->name = pa_xstrdup(name); s->proplist = pa_proplist_copy(data->proplist); s->driver = pa_xstrdup(pa_path_get_filename(data->driver)); @@ -427,13 +428,21 @@ int pa_source_update_status(pa_source*s) { } /* Called from main context */ -int pa_source_suspend(pa_source *s, pa_bool_t suspend) { +int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause) { pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); + pa_assert(cause != 0); if (s->monitor_of) return -PA_ERR_NOTSUPPORTED; + if (suspend) + s->suspend_cause |= cause; + else + s->suspend_cause &= ~cause; + + pa_log_debug("Suspend cause of source %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming"); + if (suspend) return source_set_state(s, PA_SOURCE_SUSPENDED); else @@ -1032,12 +1041,13 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ } /* Called from main thread */ -int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) { +int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) { uint32_t idx; pa_source *source; int ret = 0; pa_core_assert_ref(c); + pa_assert(cause != 0); for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx))) { int r; @@ -1045,7 +1055,7 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) { if (source->monitor_of) continue; - if ((r = pa_source_suspend(source, suspend)) < 0) + if ((r = pa_source_suspend(source, suspend, cause)) < 0) ret = r; } diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 2978f57b..1fbed70e 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -58,6 +58,7 @@ struct pa_source { pa_core *core; pa_source_state_t state; pa_source_flags_t flags; + pa_suspend_cause_t suspend_cause; char *name; char *driver; /* may be NULL */ @@ -231,8 +232,8 @@ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t size_t pa_source_get_max_rewind(pa_source *s); int pa_source_update_status(pa_source*s); -int pa_source_suspend(pa_source *s, pa_bool_t suspend); -int pa_source_suspend_all(pa_core *c, pa_bool_t suspend); +int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause); +int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause); void pa_source_set_volume(pa_source *source, const pa_cvolume *volume); const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh); -- cgit From 561c0af8518dd8a2bac07284d306b0970c304f9f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 5 Jun 2009 19:05:42 +0200 Subject: alsa: monitor device reservation status and resume automatically when device becomes unused --- src/modules/alsa/alsa-sink.c | 65 ++++++++++++++++++++++++++++++++++++++++-- src/modules/alsa/alsa-source.c | 65 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index b1adc528..98ebac3f 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -116,6 +116,8 @@ struct userdata { pa_reserve_wrapper *reserve; pa_hook_slot *reserve_slot; + pa_reserve_monitor_wrapper *monitor; + pa_hook_slot *monitor_slot; }; static void userdata_free(struct userdata *u); @@ -185,6 +187,57 @@ static int reserve_init(struct userdata *u, const char *dname) { return 0; } +static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) { + pa_bool_t b; + + pa_assert(w); + pa_assert(u); + + b = PA_PTR_TO_UINT(busy) && !u->reserve; + + pa_sink_suspend(u->sink, b, PA_SUSPEND_APPLICATION); + return PA_HOOK_OK; +} + +static void monitor_done(struct userdata *u) { + pa_assert(u); + + if (u->monitor_slot) { + pa_hook_slot_free(u->monitor_slot); + u->monitor_slot = NULL; + } + + if (u->monitor) { + pa_reserve_monitor_wrapper_unref(u->monitor); + u->monitor = NULL; + } +} + +static int reserve_monitor_init(struct userdata *u, const char *dname) { + char *rname; + + pa_assert(u); + pa_assert(dname); + + if (pa_in_system_mode()) + return 0; + + /* We are resuming, try to lock the device */ + if (!(rname = pa_alsa_get_reserve_name(dname))) + return 0; + + u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname); + pa_xfree(rname); + + if (!(u->monitor)) + return -1; + + pa_assert(!u->monitor_slot); + u->monitor_slot = pa_hook_connect(pa_reserve_monitor_wrapper_hook(u->monitor), PA_HOOK_NORMAL, (pa_hook_cb_t) monitor_cb, u); + + return 0; +} + static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; @@ -1580,9 +1633,14 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_rtclock_usec(), TRUE); - if (reserve_init(u, pa_modargs_get_value( - ma, "device_id", - pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0) + dev_id = pa_modargs_get_value( + ma, "device_id", + pa_modargs_get_value(ma, "device", DEFAULT_DEVICE)); + + if (reserve_init(u, dev_id) < 0) + goto fail; + + if (reserve_monitor_init(u, dev_id) < 0) goto fail; b = use_mmap; @@ -1828,6 +1886,7 @@ static void userdata_free(struct userdata *u) { pa_smoother_free(u->smoother); reserve_done(u); + monitor_done(u); pa_xfree(u->device_name); pa_xfree(u); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 68f697d2..277b1103 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -114,6 +114,8 @@ struct userdata { pa_reserve_wrapper *reserve; pa_hook_slot *reserve_slot; + pa_reserve_monitor_wrapper *monitor; + pa_hook_slot *monitor_slot; }; static void userdata_free(struct userdata *u); @@ -183,6 +185,57 @@ static int reserve_init(struct userdata *u, const char *dname) { return 0; } +static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) { + pa_bool_t b; + + pa_assert(w); + pa_assert(u); + + b = PA_PTR_TO_UINT(busy) && !u->reserve; + + pa_source_suspend(u->source, b, PA_SUSPEND_APPLICATION); + return PA_HOOK_OK; +} + +static void monitor_done(struct userdata *u) { + pa_assert(u); + + if (u->monitor_slot) { + pa_hook_slot_free(u->monitor_slot); + u->monitor_slot = NULL; + } + + if (u->monitor) { + pa_reserve_monitor_wrapper_unref(u->monitor); + u->monitor = NULL; + } +} + +static int reserve_monitor_init(struct userdata *u, const char *dname) { + char *rname; + + pa_assert(u); + pa_assert(dname); + + if (pa_in_system_mode()) + return 0; + + /* We are resuming, try to lock the device */ + if (!(rname = pa_alsa_get_reserve_name(dname))) + return 0; + + u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname); + pa_xfree(rname); + + if (!(u->monitor)) + return -1; + + pa_assert(!u->monitor_slot); + u->monitor_slot = pa_hook_connect(pa_reserve_monitor_wrapper_hook(u->monitor), PA_HOOK_NORMAL, (pa_hook_cb_t) monitor_cb, u); + + return 0; +} + static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; pa_assert(u); @@ -1438,9 +1491,14 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_rtclock_usec(), FALSE); - if (reserve_init(u, pa_modargs_get_value( - ma, "device_id", - pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0) + dev_id = pa_modargs_get_value( + ma, "device_id", + pa_modargs_get_value(ma, "device", DEFAULT_DEVICE)); + + if (reserve_init(u, dev_id) < 0) + goto fail; + + if (reserve_monitor_init(u, dev_id) < 0) goto fail; b = use_mmap; @@ -1676,6 +1734,7 @@ static void userdata_free(struct userdata *u) { pa_smoother_free(u->smoother); reserve_done(u); + monitor_done(u); pa_xfree(u->device_name); pa_xfree(u); -- cgit From f398407544b7eef1cad4a1e79ccd3c29ea1beb81 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 5 Jun 2009 19:24:40 +0200 Subject: augment: try to deduce the media role from the menu category --- src/modules/module-augment-properties.c | 73 ++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/modules/module-augment-properties.c b/src/modules/module-augment-properties.c index c3e5997a..15aa3a1e 100644 --- a/src/modules/module-augment-properties.c +++ b/src/modules/module-augment-properties.c @@ -58,6 +58,7 @@ struct rule { char *process_name; char *application_name; char *icon_name; + char *role; pa_proplist *proplist; }; @@ -72,12 +73,21 @@ static void rule_free(struct rule *r) { pa_xfree(r->process_name); pa_xfree(r->application_name); pa_xfree(r->icon_name); + pa_xfree(r->role); if (r->proplist) pa_proplist_free(r->proplist); pa_xfree(r); } -static int parse_properties(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { +static int parse_properties( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + struct rule *r = userdata; pa_proplist *n; @@ -93,11 +103,56 @@ static int parse_properties(const char *filename, unsigned line, const char *sec return 0; } -static int check_type(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { +static int parse_categories( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + struct rule *r = userdata; + const char *state = NULL; + char *c; + + while ((c = pa_split(rvalue, ";", &state))) { + + if (pa_streq(c, "Game")) { + pa_xfree(r->role); + r->role = pa_xstrdup("game"); + } else if (pa_streq(c, "Telephony")) { + pa_xfree(r->role); + r->role = pa_xstrdup("phone"); + } + + pa_xfree(c); + } + + return 0; +} + +static int check_type( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + return pa_streq(rvalue, "Application") ? 0 : -1; } -static int catch_all(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { +static int catch_all( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + return 0; } @@ -109,6 +164,7 @@ static void update_rule(struct rule *r) { { "Icon", pa_config_parse_string, NULL, "Desktop Entry" }, { "Type", check_type, NULL, "Desktop Entry" }, { "X-PulseAudio-Properties", parse_properties, NULL, "Desktop Entry" }, + { "Categories", parse_categories, NULL, "Desktop Entry" }, { NULL, catch_all, NULL, NULL }, { NULL, NULL, NULL, NULL }, }; @@ -131,7 +187,8 @@ static void update_rule(struct rule *r) { r->mtime = st.st_mtime; pa_xfree(r->application_name); pa_xfree(r->icon_name); - r->application_name = r->icon_name = NULL; + pa_xfree(r->role); + r->application_name = r->icon_name = r->role = NULL; if (r->proplist) pa_proplist_clear(r->proplist); @@ -151,6 +208,9 @@ static void apply_rule(struct rule *r, pa_proplist *p) { if (!r->good) return; + if (r->proplist) + pa_proplist_update(p, PA_UPDATE_MERGE, r->proplist); + if (r->icon_name) if (!pa_proplist_contains(p, PA_PROP_APPLICATION_ICON_NAME)) pa_proplist_sets(p, PA_PROP_APPLICATION_ICON_NAME, r->icon_name); @@ -164,8 +224,9 @@ static void apply_rule(struct rule *r, pa_proplist *p) { pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, r->application_name); } - if (r->proplist) - pa_proplist_update(p, PA_UPDATE_MERGE, r->proplist); + if (r->role) + if (!pa_proplist_contains(p, PA_PROP_MEDIA_ROLE)) + pa_proplist_sets(p, PA_PROP_MEDIA_ROLE, r->role); } static void make_room(pa_hashmap *cache) { -- cgit From e5dd9dfe682000f75d2770c21fd26d6cc56e3c4d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 5 Jun 2009 19:25:15 +0200 Subject: rtp: remove gcc warning --- src/modules/rtp/module-rtp-send.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index cdd2c57d..39ee4d75 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -347,8 +347,8 @@ int pa__init(pa_module*m) { o->push = source_output_push; o->kill = source_output_kill; - pa_log_info("Configured source latency of %lu ms.", - pa_source_output_set_requested_latency(o, pa_bytes_to_usec(mtu, &o->sample_spec)) / PA_USEC_PER_MSEC); + pa_log_info("Configured source latency of %llu ms.", + (unsigned long long) pa_source_output_set_requested_latency(o, pa_bytes_to_usec(mtu, &o->sample_spec)) / PA_USEC_PER_MSEC); m->userdata = o->userdata = u = pa_xnew(struct userdata, 1); u->module = m; -- cgit From 8080ab12597cfbffe9327271236b85054765a2d8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 6 Jun 2009 03:26:07 +0200 Subject: udev: add module-udev-detect for discovering and configuring devices --- src/Makefile.am | 11 ++ src/modules/module-udev-detect.c | 348 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 src/modules/module-udev-detect.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index c0e1806f..38395e7a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1070,6 +1070,11 @@ modlibexec_LTLIBRARIES += \ module-hal-detect.la endif +if HAVE_UDEV +modlibexec_LTLIBRARIES += \ + module-udev-detect.la +endif + if HAVE_DBUS modlibexec_LTLIBRARIES += \ module-rygel-media-server.la @@ -1155,6 +1160,7 @@ SYMDEF_FILES = \ modules/module-rescue-streams-symdef.h \ modules/module-suspend-on-idle-symdef.h \ modules/module-hal-detect-symdef.h \ + modules/module-udev-detect-symdef.h \ modules/bluetooth/module-bluetooth-proximity-symdef.h \ modules/bluetooth/module-bluetooth-discover-symdef.h \ modules/bluetooth/module-bluetooth-device-symdef.h \ @@ -1518,6 +1524,11 @@ module_hal_detect_la_LDFLAGS = $(MODULE_LDFLAGS) module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS) +module_udev_detect_la_SOURCES = modules/module-udev-detect.c +module_udev_detect_la_LDFLAGS = $(MODULE_LDFLAGS) +module_udev_detect_la_LIBADD = $(AM_LIBADD) $(UDEV_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_udev_detect_la_CFLAGS = $(AM_CFLAGS) $(UDEV_CFLAGS) + module_console_kit_la_SOURCES = modules/module-console-kit.c module_console_kit_la_LDFLAGS = $(MODULE_LDFLAGS) module_console_kit_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c new file mode 100644 index 00000000..29c94e12 --- /dev/null +++ b/src/modules/module-udev-detect.c @@ -0,0 +1,348 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include "module-udev-detect-symdef.h" + +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); + +struct device { + char *path; + char *card_name; + uint32_t module; +}; + +struct userdata { + pa_core *core; + pa_hashmap *devices; + pa_bool_t use_tsched; + struct udev* udev; + struct udev_monitor *monitor; + pa_io_event *io; +}; + +static const char* const valid_modargs[] = { + "tsched", + NULL +}; + +static void device_free(struct device *d) { + pa_assert(d); + + pa_xfree(d->path); + pa_xfree(d->card_name); + pa_xfree(d); +} + +static const char *path_get_card_id(const char *path) { + const char *e; + + if (!path) + return NULL; + + if (!(e = strrchr(path, '/'))) + return NULL; + + if (!pa_startswith(e, "/card")) + return NULL; + + return e + 5; +} + +static void verify_access(struct userdata *u, struct device *d) { + char *cd; + int r; + pa_card *card; + + pa_assert(u); + pa_assert(d); + + cd = pa_sprintf_malloc("%s/controlC%s", d->path, path_get_card_id(d->path)); + r = access(cd, W_OK); + pa_log_info("%s is accessible: %s", cd, pa_yes_no(r >= 0)); + pa_xfree(cd); + + if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) + pa_card_suspend(card, r < 0, PA_SUSPEND_SESSION); +} + +static void card_changed(struct userdata *u, struct udev_device *dev) { + struct device *d; + const char *path; + const char *t; + char *card_name, *args; + pa_module *m; + char *n; + + pa_assert(u); + pa_assert(dev); + + path = udev_device_get_devpath(dev); + + if ((d = pa_hashmap_get(u->devices, path))) { + verify_access(u, d); + return; + } + + 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); + + card_name = pa_sprintf_malloc("alsa_card.%s", n); + args = pa_sprintf_malloc("device_id=\"%s\" " + "name=\"%s\" " + "card_name=\"%s\" " + "tsched=%i " + "card_properties=\"module-udev-detect.discovered=1\"", + path_get_card_id(path), + n, + card_name, + (int) u->use_tsched); + + pa_log_debug("Loading module-alsa-card with arguments '%s'", args); + m = pa_module_load(u->core, "module-alsa-card", args); + pa_xfree(args); + + if (m) { + pa_log_info("Card %s (%s) added.", path, n); + + d = pa_xnew(struct device, 1); + d->path = pa_xstrdup(path); + d->card_name = card_name; + d->module = m->index; + + pa_hashmap_put(u->devices, d->path, d); + } else + pa_xfree(card_name); + + pa_xfree(n); +} + +static void remove_card(struct userdata *u, struct udev_device *dev) { + struct device *d; + + pa_assert(u); + pa_assert(dev); + + if (!(d = pa_hashmap_remove(u->devices, udev_device_get_devpath(dev)))) + return; + + pa_log_info("Card %s removed.", d->path); + pa_module_unload_request_by_index(u->core, d->module, TRUE); + device_free(d); +} + +static void process_device(struct userdata *u, struct udev_device *dev) { + const char *action, *ff; + + pa_assert(u); + pa_assert(dev); + + if (udev_device_get_property_value(dev, "PULSE_IGNORE")) { + pa_log_info("Ignoring %s, because marked so.", udev_device_get_devpath(dev)); + return; + } + + if ((ff = udev_device_get_property_value(dev, "SOUND_FORM_FACTOR")) && + pa_streq(ff, "modem")) { + pa_log_info("Ignoring %s, because it is a modem.", udev_device_get_devpath(dev)); + return; + } + + action = udev_device_get_action(dev); + + if (action && pa_streq(action, "remove")) + remove_card(u, dev); + else if ((!action || pa_streq(action, "change")) && + udev_device_get_property_value(dev, "SOUND_INITIALIZED")) + card_changed(u, dev); + + /* For an explanation why we don't look for 'add' events here + * have a look into /lib/udev/rules.d/78-sound-card.rules! */ +} + +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 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."); + return; + } + + if (!path_get_card_id(udev_device_get_devpath(dev))) + return; + + process_device(u, dev); + udev_device_unref(dev); +} + +int pa__init(pa_module *m) { + struct userdata *u = NULL; + pa_modargs *ma; + struct udev_enumerate *enumerate = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + int fd; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + u->core = m->core; + u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + u->use_tsched = TRUE; + + if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) { + pa_log("Failed to parse tsched argument."); + goto fail; + } + + if (!(u->udev = udev_new())) { + pa_log("Failed to initialize udev library."); + goto fail; + } + + if (!(u->monitor = udev_monitor_new_from_netlink(u->udev, "udev"))) { + pa_log("Failed to initialize monitor."); + goto fail; + } + + errno = 0; + if (udev_monitor_enable_receiving(u->monitor) < 0) { + pa_log("Failed to enable monitor: %s", pa_cstrerror(errno)); + if (errno == EPERM) + pa_log_info("Most likely your kernel is simply too old and " + "allows only priviliged processes to listen to device events. " + "Please upgrade your kernel to at least 2.6.30."); + goto fail; + } + + if ((fd = udev_monitor_get_fd(u->monitor)) < 0) { + pa_log("Failed to get udev monitor fd."); + goto fail; + } + + pa_assert_se(u->io = u->core->mainloop->io_new(u->core->mainloop, fd, PA_IO_EVENT_INPUT, monitor_cb, u)); + + if (!(enumerate = udev_enumerate_new(u->udev))) { + pa_log("Failed to initialize udev enumerator."); + goto fail; + } + + if (udev_enumerate_add_match_subsystem(enumerate, "sound") < 0) { + pa_log("Failed to match to subsystem."); + goto fail; + } + + if (udev_enumerate_scan_devices(enumerate) < 0) { + pa_log("Failed to scan for devices."); + goto fail; + } + + first = udev_enumerate_get_list_entry(enumerate); + udev_list_entry_foreach(item, first) + process_path(u, udev_list_entry_get_name(item)); + + udev_enumerate_unref(enumerate); + + pa_log_info("Loaded %u modules.", pa_hashmap_size(u->devices)); + + pa_modargs_free(ma); + + return 0; + +fail: + + if (enumerate) + udev_enumerate_unref(enumerate); + + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; +} + +void pa__done(pa_module *m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->monitor) + udev_monitor_unref(u->monitor); + + if (u->udev) + udev_unref(u->udev); + + if (u->devices) { + struct device *d; + + while ((d = pa_hashmap_steal_first(u->devices))) + device_free(d); + + pa_hashmap_free(u->devices, NULL, NULL); + } + + pa_xfree(u); +} -- cgit From e84644aa88acd7158bb174946dbff630ff2389ad Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 6 Jun 2009 03:26:55 +0200 Subject: hal,udev: deprecate HAL support in favour of udev --- src/daemon/default.pa.in | 6 +++--- src/modules/module-hal-detect.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in index fa0683e1..a35ff8ff 100755 --- a/src/daemon/default.pa.in +++ b/src/daemon/default.pa.in @@ -49,11 +49,11 @@ load-module module-augment-properties #load-module module-pipe-sink ### Automatically load driver modules depending on the hardware available -.ifexists module-hal-detect@PA_SOEXT@ -load-module module-hal-detect +.ifexists module-udev-detect@PA_SOEXT@ +load-module module-udev-detect .else ### Alternatively use the static hardware detection module (for systems that -### lack HAL support) +### lack udev support) load-module module-detect .endif diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index 9ac87050..658b3e55 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -64,6 +64,7 @@ PA_MODULE_USAGE("api= " #elif defined(HAVE_OSS) PA_MODULE_USAGE("api="); #endif +PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!"); struct device { char *udi, *originating_udi; @@ -232,7 +233,7 @@ static int hal_device_load_alsa(struct userdata *u, const char *udi, struct devi goto fail; card_name = pa_sprintf_malloc("alsa_card.%s", strip_udi(originating_udi)); - args = pa_sprintf_malloc("device_id=%u name=%s card_name=%s tsched=%i", card, strip_udi(originating_udi), card_name, (int) u->use_tsched); + args = pa_sprintf_malloc("device_id=%u name=\"%s\" card_name=\"%s\" tsched=%i card_properties=\"module-hal-detect.discovered=1\"", card, strip_udi(originating_udi), card_name, (int) u->use_tsched); pa_log_debug("Loading module-alsa-card with arguments '%s'", args); m = pa_module_load(u->core, "module-alsa-card", args); -- cgit From 496be212ad645999647c432a856c906b86d41487 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 6 Jun 2009 15:30:24 +0200 Subject: hashmap: introduce PA_HASHMAP_FOREACH macro --- src/pulsecore/hashmap.h | 4 ++++ src/pulsecore/idxset.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h index 08e18ead..828e2448 100644 --- a/src/pulsecore/hashmap.h +++ b/src/pulsecore/hashmap.h @@ -65,4 +65,8 @@ void *pa_hashmap_steal_first(pa_hashmap *h); /* Return the oldest entry in the hashmap */ void* pa_hashmap_first(pa_hashmap *h); +/* A macro to ease iteration through all entries */ +#define PA_HASHMAP_FOREACH(e, h, state) \ + for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL)) + #endif diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h index 6b9ff472..a6179fcf 100644 --- a/src/pulsecore/idxset.h +++ b/src/pulsecore/idxset.h @@ -103,7 +103,7 @@ unsigned pa_idxset_size(pa_idxset*s); /* Return TRUE of the idxset is empty */ pa_bool_t pa_idxset_isempty(pa_idxset *s); - +/* A macro to ease iteration through all entries */ #define PA_IDXSET_FOREACH(e, s, idx) \ for ((e) = pa_idxset_first((s), &(idx)); (e); (e) = pa_idxset_next((s), &(idx))) -- cgit From 69eab1e057e000951516cfd381846c33abc8aaf1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 6 Jun 2009 15:31:46 +0200 Subject: core: suppress suspending/resume when we are already in the right state --- src/pulsecore/sink.c | 3 +++ src/pulsecore/source.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'src') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 3c4adc6c..a087e42d 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -510,6 +510,9 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) { else s->suspend_cause &= ~cause; + if ((pa_sink_get_state(s) == PA_SINK_SUSPENDED) == !!s->suspend_cause) + return 0; + pa_log_debug("Suspend cause of sink %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming"); if (s->suspend_cause) diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 4ade18f6..abb5f4d1 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -441,6 +441,9 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause) else s->suspend_cause &= ~cause; + if ((pa_source_get_state(s) == PA_SOURCE_SUSPENDED) == !!s->suspend_cause) + return 0; + pa_log_debug("Suspend cause of source %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming"); if (suspend) -- cgit From d1646f78af6a43928cb0cd6b3e8d45578c489cb1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 6 Jun 2009 15:32:45 +0200 Subject: core: monitor sources need to inherit the suspend cause from their sinks --- src/pulsecore/sink.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index a087e42d..141084d6 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -505,10 +505,13 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) { pa_assert(PA_SINK_IS_LINKED(s->state)); pa_assert(cause != 0); - if (suspend) + if (suspend) { s->suspend_cause |= cause; - else + s->monitor_source->suspend_cause |= cause; + } else { s->suspend_cause &= ~cause; + s->monitor_source->suspend_cause &= ~cause; + } if ((pa_sink_get_state(s) == PA_SINK_SUSPENDED) == !!s->suspend_cause) return 0; -- cgit From bab4b94b566f54976836b04f187d97d424f7d750 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 6 Jun 2009 15:33:54 +0200 Subject: udev: try to unsuspend devices whenever they are closed by other processes --- src/modules/module-udev-detect.c | 133 +++++++++++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 29c94e12..b667cb44 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -24,6 +24,7 @@ #endif #include +#include #include #include @@ -41,6 +42,7 @@ PA_MODULE_LOAD_ONCE(TRUE); struct device { char *path; + pa_bool_t accessible; char *card_name; uint32_t module; }; @@ -49,9 +51,13 @@ struct userdata { pa_core *core; pa_hashmap *devices; pa_bool_t use_tsched; + struct udev* udev; struct udev_monitor *monitor; - pa_io_event *io; + pa_io_event *udev_io; + + int inotify_fd; + pa_io_event *inotify_io; }; static const char* const valid_modargs[] = { @@ -84,19 +90,20 @@ static const char *path_get_card_id(const char *path) { static void verify_access(struct userdata *u, struct device *d) { char *cd; - int r; pa_card *card; pa_assert(u); pa_assert(d); - cd = pa_sprintf_malloc("%s/controlC%s", d->path, path_get_card_id(d->path)); - r = access(cd, W_OK); - pa_log_info("%s is accessible: %s", cd, pa_yes_no(r >= 0)); + if (!(card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) + return; + + cd = pa_sprintf_malloc("%s/snd/controlC%s", udev_get_dev_path(u->udev), path_get_card_id(d->path)); + d->accessible = access(cd, W_OK) >= 0; + pa_log_info("%s is accessible: %s", cd, pa_yes_no(d->accessible)); pa_xfree(cd); - if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) - pa_card_suspend(card, r < 0, PA_SUSPEND_SESSION); + pa_card_suspend(card, !d->accessible, PA_SUSPEND_SESSION); } static void card_changed(struct userdata *u, struct udev_device *dev) { @@ -146,6 +153,7 @@ static void card_changed(struct userdata *u, struct udev_device *dev) { d->path = pa_xstrdup(path); d->card_name = card_name; d->module = m->index; + d->accessible = TRUE; pa_hashmap_put(u->devices, d->path, d); } else @@ -175,13 +183,13 @@ static void process_device(struct userdata *u, struct udev_device *dev) { pa_assert(dev); if (udev_device_get_property_value(dev, "PULSE_IGNORE")) { - pa_log_info("Ignoring %s, because marked so.", udev_device_get_devpath(dev)); + pa_log_debug("Ignoring %s, because marked so.", udev_device_get_devpath(dev)); return; } if ((ff = udev_device_get_property_value(dev, "SOUND_FORM_FACTOR")) && pa_streq(ff, "modem")) { - pa_log_info("Ignoring %s, because it is a modem.", udev_device_get_devpath(dev)); + pa_log_debug("Ignoring %s, because it is a modem.", udev_device_get_devpath(dev)); return; } @@ -212,7 +220,13 @@ static void process_path(struct userdata *u, const char *path) { udev_device_unref(dev); } -static void monitor_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { +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; @@ -220,7 +234,7 @@ static void monitor_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_fl if (!(dev = udev_monitor_receive_device(u->monitor))) { pa_log("Failed to get udev device object from monitor."); - return; + goto fail; } if (!path_get_card_id(udev_device_get_devpath(dev))) @@ -228,6 +242,88 @@ static void monitor_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_fl process_device(u, dev); udev_device_unref(dev); + return; + +fail: + a->io_free(u->udev_io); + u->udev_io = NULL; +} + +static void inotify_cb( + pa_mainloop_api*a, + pa_io_event* e, + int fd, + pa_io_event_flags_t events, + void *userdata) { + + struct { + struct inotify_event e; + char name[NAME_MAX]; + } buf; + struct userdata *u = userdata; + static int type = 0; + pa_bool_t verify = FALSE; + + for (;;) { + ssize_t r; + + pa_zero(buf); + if ((r = pa_read(fd, &buf, sizeof(buf), &type)) <= 0) { + + if (r < 0 && errno == EAGAIN) + break; + + pa_log("read() from inotify failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF"); + goto fail; + } + + if ((buf.e.mask & IN_CLOSE_WRITE) && pa_startswith(buf.e.name, "pcmC")) + verify = TRUE; + } + + if (verify) { + struct device *d; + void *state; + + pa_log_debug("Verifying access."); + + PA_HASHMAP_FOREACH(d, u->devices, state) + verify_access(u, d); + } + + return; + +fail: + a->io_free(u->inotify_io); + u->inotify_io = NULL; + + if (u->inotify_fd >= 0) { + pa_close(u->inotify_fd); + u->inotify_fd = -1; + } +} + +static int setup_inotify(struct userdata *u) { + char *dev_snd; + int r; + + if ((u->inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) { + pa_log("inotify_init1() failed: %s", pa_cstrerror(errno)); + return -1; + } + + dev_snd = pa_sprintf_malloc("%s/snd", udev_get_dev_path(u->udev)); + r = inotify_add_watch(u->inotify_fd, dev_snd, IN_CLOSE_WRITE); + pa_xfree(dev_snd); + + if (r < 0) { + pa_log("inotify_add_watch() failed: %s", pa_cstrerror(errno)); + return -1; + } + + pa_assert_se(u->inotify_io = u->core->mainloop->io_new(u->core->mainloop, u->inotify_fd, PA_IO_EVENT_INPUT, inotify_cb, u)); + + return 0; } int pa__init(pa_module *m) { @@ -248,6 +344,7 @@ int pa__init(pa_module *m) { u->core = m->core; u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); u->use_tsched = TRUE; + u->inotify_fd = -1; if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) { pa_log("Failed to parse tsched argument."); @@ -259,6 +356,9 @@ int pa__init(pa_module *m) { goto fail; } + if (setup_inotify(u) < 0) + goto fail; + if (!(u->monitor = udev_monitor_new_from_netlink(u->udev, "udev"))) { pa_log("Failed to initialize monitor."); goto fail; @@ -279,7 +379,7 @@ int pa__init(pa_module *m) { goto fail; } - pa_assert_se(u->io = u->core->mainloop->io_new(u->core->mainloop, fd, PA_IO_EVENT_INPUT, monitor_cb, u)); + pa_assert_se(u->udev_io = u->core->mainloop->io_new(u->core->mainloop, fd, PA_IO_EVENT_INPUT, monitor_cb, u)); if (!(enumerate = udev_enumerate_new(u->udev))) { pa_log("Failed to initialize udev enumerator."); @@ -329,12 +429,21 @@ void pa__done(pa_module *m) { if (!(u = m->userdata)) return; + if (u->udev_io) + m->core->mainloop->io_free(u->udev_io); + if (u->monitor) udev_monitor_unref(u->monitor); if (u->udev) udev_unref(u->udev); + if (u->inotify_io) + m->core->mainloop->io_free(u->inotify_io); + + if (u->inotify_fd >= 0) + pa_close(u->inotify_fd); + if (u->devices) { struct device *d; -- cgit From 71ce195d3bb2cc1f88210fd7e22495bc38d2f72d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 7 Jun 2009 00:40:48 +0200 Subject: udev: properly initialize userdata to 0 --- src/modules/module-udev-detect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index b667cb44..1ad6fa2d 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -340,7 +340,7 @@ int pa__init(pa_module *m) { goto fail; } - m->userdata = u = pa_xnew(struct userdata, 1); + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); u->use_tsched = TRUE; -- cgit From a9b38b3530318daf44d55c5fdfdd12b2299ef215 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 7 Jun 2009 00:43:03 +0200 Subject: daemon: optionally call mlockall() on startup --- src/daemon/daemon-conf.c | 19 ++++++++++--------- src/daemon/daemon-conf.h | 3 ++- src/daemon/daemon.conf.in | 4 ++-- src/daemon/main.c | 15 +++++++++++++++ 4 files changed, 29 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index ac6cc8aa..664e4fde 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -85,6 +85,7 @@ static const pa_daemon_conf default_conf = { .system_instance = FALSE, .no_cpu_limit = FALSE, .disable_shm = FALSE, + .lock_memory = FALSE, .default_n_fragments = 4, .default_fragment_size_msec = 25, .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 }, @@ -446,6 +447,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { { "no-cpu-limit", pa_config_parse_bool, &c->no_cpu_limit, NULL }, { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL }, { "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL }, + { "lock-memory", pa_config_parse_bool, &c->lock_memory, NULL }, { "exit-idle-time", pa_config_parse_int, &c->exit_idle_time, NULL }, { "scache-idle-time", pa_config_parse_int, &c->scache_idle_time, NULL }, { "realtime-priority", parse_rtprio, c, NULL }, @@ -595,16 +597,14 @@ FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) { return f; } - -static const char* const log_level_to_string[] = { - [PA_LOG_DEBUG] = "debug", - [PA_LOG_INFO] = "info", - [PA_LOG_NOTICE] = "notice", - [PA_LOG_WARN] = "warning", - [PA_LOG_ERROR] = "error" -}; - char *pa_daemon_conf_dump(pa_daemon_conf *c) { + static const char* const log_level_to_string[] = { + [PA_LOG_DEBUG] = "debug", + [PA_LOG_INFO] = "info", + [PA_LOG_NOTICE] = "notice", + [PA_LOG_WARN] = "warning", + [PA_LOG_ERROR] = "error" + }; pa_strbuf *s; char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; @@ -630,6 +630,7 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) { pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit)); pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm)); pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes)); + pa_strbuf_printf(s, "lock-memory = %s\n", pa_yes_no(c->lock_memory)); pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time); pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time); pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path)); diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index 9cec189f..dd69e048 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -73,7 +73,8 @@ typedef struct pa_daemon_conf { disallow_exit, log_meta, log_time, - flat_volumes; + flat_volumes, + lock_memory; int exit_idle_time, scache_idle_time, auto_log_target, diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index fcd2513a..746ea76c 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -27,6 +27,8 @@ ; system-instance = no ; disable-shm = no ; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB +; lock-memory = no +; no-cpu-limit = no ; high-priority = yes ; nice-level = -11 @@ -55,8 +57,6 @@ ; flat-volumes = yes -; no-cpu-limit = no - ; rlimit-fsize = -1 ; rlimit-data = -1 ; rlimit-stack = -1 diff --git a/src/daemon/main.c b/src/daemon/main.c index 3e50baad..58f8d660 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -40,6 +40,10 @@ #include +#ifdef HAVE_SYS_MMAN_H +#include +#endif + #ifdef HAVE_SYS_IOCTL_H #include #endif @@ -960,6 +964,17 @@ int main(int argc, char *argv[]) { pa_rtsig_configure(SIGRTMIN, SIGRTMAX-1); #endif + if (conf->lock_memory) { +#ifdef HAVE_SYS_MMAN_H + if (mlockall(MCL_FUTURE) < 0) + pa_log_warn("mlockall() failed: %s", pa_cstrerror(errno)); + else + pa_log_info("Sucessfully locked process into memory."); +#else + pa_log_warn("Memory locking requested but not supported on platform."); +#endif + } + pa_memtrap_install(); pa_assert_se(mainloop = pa_mainloop_new()); -- cgit From 78bccde2264c439702b103fd8491d9f56c79f54f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 7 Jun 2009 00:44:16 +0200 Subject: conf: remove obsolete module-idle-time directive from default config file/man page --- src/daemon/daemon.conf.in | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index 746ea76c..d119716d 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -37,7 +37,6 @@ ; realtime-priority = 5 ; exit-idle-time = 20 -; module-idle-time = 20 ; scache-idle-time = 20 ; dl-search-path = (depends on architecture) -- cgit From 8eaea3aa6471b7d0e78d110752847db5b213c85a Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 14 May 2009 09:42:52 +0300 Subject: optimization: Optimized pa_sink_render_full. This is finally the latest version of the patch. --- src/pulsecore/sink.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 141084d6..db9b4f3a 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1025,14 +1025,13 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { pa_memchunk chunk; size_t l, d; pa_memchunk_make_writable(result, length); - result->length = length; l = length - result->length; d = result->index + result->length; while (l > 0) { chunk = *result; - chunk.index += d; - chunk.length -= d - result->index; + chunk.index = d; + chunk.length = l; pa_sink_render_into(s, &chunk); -- cgit From ab5adce7a014f57df829491d06acafbfec3afb5d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 7 Jun 2009 23:24:02 +0200 Subject: rtstutter: increase log level by default --- src/tests/rtstutter.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c index f04d43af..a4b5d596 100644 --- a/src/tests/rtstutter.c +++ b/src/tests/rtstutter.c @@ -93,6 +93,8 @@ static void* work(void *p) { int main(int argc, char*argv[]) { unsigned n; + pa_log_set_level(PA_LOG_DEBUG); + srand((unsigned) time(NULL)); if (argc >= 3) { -- cgit From 587fc2ab1c7b53ddd28f35e19aad55caa804de1f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 8 Jun 2009 00:02:15 +0200 Subject: core: make sure soft mute status stays in sync with hw mute status This should close rhbz #494851, mandriva bz #51234. Probably the same as our own #572, launchpad #352732. --- src/modules/alsa/alsa-sink.c | 2 +- src/modules/alsa/alsa-source.c | 2 +- src/pulsecore/sink.c | 6 +++++- src/pulsecore/source.c | 6 +++++- 4 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 98ebac3f..59a5ca75 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1199,7 +1199,7 @@ fail: static void sink_get_mute_cb(pa_sink *s) { struct userdata *u = s->userdata; - int err, sw; + int err, sw = 0; pa_assert(u); pa_assert(u->mixer_elem); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 277b1103..c176309e 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1158,7 +1158,7 @@ fail: static void source_get_mute_cb(pa_source *s) { struct userdata *u = s->userdata; - int err, sw; + int err, sw = 0; pa_assert(u); pa_assert(u->mixer_elem); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index db9b4f3a..5d54d839 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1365,8 +1365,12 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) { pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0); - if (old_muted != s->muted) + if (old_muted != s->muted) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + + /* Make sure the soft mute status stays in sync */ + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); + } } return s->muted; diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index abb5f4d1..53697c57 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -768,8 +768,12 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) { pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0); - if (old_muted != s->muted) + if (old_muted != s->muted) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + + /* Make sure the soft mute status stays in sync */ + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); + } } return s->muted; -- cgit From e7a6d53118315ff12896707e42528bda5e5d9eab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 8 Jun 2009 00:12:20 +0200 Subject: core: replace tabs by spaces --- src/pulsecore/sink.c | 62 ++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 5d54d839..13f0e11e 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -973,21 +973,21 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { n = fill_mix_info(s, &length1st, info, MAX_MIX_CHANNELS); if (n == 0) { - pa_silence_memchunk_get(&s->core->silence_cache, - s->core->mempool, - result, - &s->sample_spec, - length1st); + pa_silence_memchunk_get(&s->core->silence_cache, + s->core->mempool, + result, + &s->sample_spec, + length1st); } else if (n == 1) { - pa_cvolume volume; + pa_cvolume volume; - *result = info[0].chunk; - pa_memblock_ref(result->memblock); + *result = info[0].chunk; + pa_memblock_ref(result->memblock); - if (result->length > length) - result->length = length; + if (result->length > length) + result->length = length; - pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); + pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) { if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) { @@ -1005,10 +1005,10 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { } else { void *ptr; - result->index = 0; - result->memblock = pa_memblock_new(s->core->mempool, length); + result->index = 0; + result->memblock = pa_memblock_new(s->core->mempool, length); - ptr = pa_memblock_acquire(result->memblock); + ptr = pa_memblock_acquire(result->memblock); result->length = pa_mix(info, n, (uint8_t*) ptr + result->index, length1st, @@ -1022,23 +1022,23 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { inputs_drop(s, info, n, result); if (result->length < length) { - pa_memchunk chunk; - size_t l, d; - pa_memchunk_make_writable(result, length); - - l = length - result->length; - d = result->index + result->length; - while (l > 0) { - chunk = *result; - chunk.index = d; - chunk.length = l; - - pa_sink_render_into(s, &chunk); - - d += chunk.length; - l -= chunk.length; - } - result->length = length; + pa_memchunk chunk; + size_t l, d; + pa_memchunk_make_writable(result, length); + + l = length - result->length; + d = result->index + result->length; + while (l > 0) { + chunk = *result; + chunk.index = d; + chunk.length = l; + + pa_sink_render_into(s, &chunk); + + d += chunk.length; + l -= chunk.length; + } + result->length = length; } pa_sink_unref(s); -- cgit From f13bbd576fef8911275c8eed41ef425163b70398 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 8 Jun 2009 16:58:45 +0200 Subject: prop: introduce new PA_PROP_DEVICE_INTENDED_ROLES property --- src/modules/bluetooth/module-bluetooth-device.c | 4 ++++ src/modules/module-raop-sink.c | 1 + src/pulse/proplist.h | 3 +++ src/pulsecore/card.c | 1 + src/pulsecore/sink.c | 17 +++++++++++++++++ src/pulsecore/sink.h | 1 + src/pulsecore/source.c | 1 + 7 files changed, 28 insertions(+) (limited to 'src') diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index dbec00d4..40093cf2 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1622,6 +1622,8 @@ static int add_sink(struct userdata *u) { data.module = u->module; pa_sink_new_data_set_sample_spec(&data, &u->sample_spec); pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco"); + if (u->profile == PROFILE_HSP) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); data.card = u->card; data.name = get_name("sink", u->modargs, u->address, &b); data.namereg_fail = b; @@ -1680,6 +1682,8 @@ static int add_source(struct userdata *u) { data.module = u->module; pa_source_new_data_set_sample_spec(&data, &u->sample_spec); pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "hsp"); + if (u->profile == PROFILE_HSP) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); data.card = u->card; data.name = get_name("source", u->modargs, u->address, &b); data.namereg_fail = b; diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 052a3a5e..54de42c2 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -583,6 +583,7 @@ int pa__init(pa_module*m) { pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_sink_new_data_set_sample_spec(&data, &ss); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "music"); if ((desc = pa_modargs_get_value(ma, "description", NULL))) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, desc); else diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 4c791dce..bc4dbd8a 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -206,6 +206,9 @@ PA_C_DECL_BEGIN /** For devices: profile identifier for the profile this devices is in. e.g. "analog-stereo", "analog-surround-40", "iec958-stereo", ...*/ #define PA_PROP_DEVICE_PROFILE_NAME "device.profile.name" +/** For devices: intended use. A comma seperated list of roles (see PA_PROP_MEDIA_ROLE) this device is particularly well suited for, due to latency, quality or form factor. \since 0.9.16 */ +#define PA_PROP_DEVICE_INTENDED_ROLES "device.intended_roles" + /** For devices: human readable one-line description of the profile this device is in. e.g. "Analog Stereo", ... */ #define PA_PROP_DEVICE_PROFILE_DESCRIPTION "device.profile.description" diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c index 59b8cda6..9c16ef2d 100644 --- a/src/pulsecore/card.c +++ b/src/pulsecore/card.c @@ -164,6 +164,7 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) { pa_device_init_description(c->proplist); pa_device_init_icon(c->proplist, TRUE); + pa_device_init_intended_roles(c->proplist); pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 13f0e11e..1da094af 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -177,6 +177,7 @@ pa_sink* pa_sink_new( pa_device_init_description(data->proplist); pa_device_init_icon(data->proplist, TRUE); + pa_device_init_intended_roles(data->proplist); if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) { pa_xfree(s); @@ -2287,3 +2288,19 @@ pa_bool_t pa_device_init_description(pa_proplist *p) { return FALSE; } + +pa_bool_t pa_device_init_intended_roles(pa_proplist *p) { + const char *s; + pa_assert(p); + + if (pa_proplist_contains(p, PA_PROP_DEVICE_INTENDED_ROLES)) + return TRUE; + + if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) + if (pa_streq(s, "handset") || pa_streq(s, "hands-free")) { + pa_proplist_sets(p, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); + return TRUE; + } + + return FALSE; +} diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 4dce3f93..0f32d163 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -241,6 +241,7 @@ void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted); pa_bool_t pa_device_init_description(pa_proplist *p); pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink); +pa_bool_t pa_device_init_intended_roles(pa_proplist *p); /**** May be called by everyone, from main context */ diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 53697c57..d35c8523 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -167,6 +167,7 @@ pa_source* pa_source_new( pa_device_init_description(data->proplist); pa_device_init_icon(data->proplist, FALSE); + pa_device_init_intended_roles(data->proplist); if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) { pa_xfree(s); -- cgit From 3c4c1f4945b309b3eba11ca67c010469c7ff9256 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 8 Jun 2009 16:59:47 +0200 Subject: udev: reshuffle the properties we read from udev a bit --- src/modules/udev-util.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/udev-util.c b/src/modules/udev-util.c index 144ad797..de8f5f2f 100644 --- a/src/modules/udev-util.c +++ b/src/modules/udev-util.c @@ -84,6 +84,19 @@ int pa_udev_get_info(pa_core *core, pa_proplist *p, int card_idx) { goto finish; } + if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS_PATH)) + if (((v = udev_device_get_property_value(card, "ID_PATH")) && *v) || + (v = udev_device_get_devpath(card))) + pa_proplist_sets(p, PA_PROP_DEVICE_BUS_PATH, v); + + if (!pa_proplist_contains(p, "sysfs.path")) + if ((v = udev_device_get_devpath(card))) + pa_proplist_sets(p, "sysfs.path", v); + + if (!pa_proplist_contains(p, "udev.id")) + if ((v = udev_device_get_property_value(card, "ID_ID")) && *v) + pa_proplist_sets(p, "udev.id", v); + if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS)) if ((v = udev_device_get_property_value(card, "ID_BUS")) && *v) pa_proplist_sets(p, PA_PROP_DEVICE_BUS, v); @@ -114,15 +127,15 @@ int pa_udev_get_info(pa_core *core, pa_proplist *p, int card_idx) { if ((v = udev_device_get_property_value(card, "ID_SERIAL")) && *v) pa_proplist_sets(p, PA_PROP_DEVICE_SERIAL, v); + if (!pa_proplist_contains(p, PA_PROP_DEVICE_CLASS)) + if ((v = udev_device_get_property_value(card, "SOUND_CLASS")) && *v) + pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, v); + if (!pa_proplist_contains(p, PA_PROP_DEVICE_FORM_FACTOR)) if ((v = udev_device_get_property_value(card, "SOUND_FORM_FACTOR")) && *v) pa_proplist_sets(p, PA_PROP_DEVICE_FORM_FACTOR, v); - if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS_PATH)) - if ((v = udev_device_get_devpath(card))) - pa_proplist_sets(p, PA_PROP_DEVICE_BUS_PATH, v); - - /* This is normaly not set by th udev rules but may be useful to + /* This is normaly not set by the udev rules but may be useful to * allow administrators to overwrite the device description.*/ if (!pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION)) if ((v = udev_device_get_property_value(card, "SOUND_DESCRIPTION")) && *v) -- cgit From 89e3adf8cd3e0901a786274c584c89dd082eab09 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 8 Jun 2009 18:22:19 +0200 Subject: sample: fix build on BE archs --- src/pulsecore/sample-util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index dda38834..5b8ccf59 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -1182,7 +1182,7 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, case PA_SAMPLE_S24LE: case PA_SAMPLE_S24BE: case PA_SAMPLE_S24_32LE: - case PA_SAMPLE_S24_32RE: + case PA_SAMPLE_S24_32BE: case PA_SAMPLE_FLOAT32LE: case PA_SAMPLE_FLOAT32BE: cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0); -- cgit From 1e8a374f2f26edbe68d2fc3c049ef7ccdf855d56 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 10 Jun 2009 23:22:12 +0200 Subject: alsa: fix bad memory access for devices that lack a mixer --- src/modules/alsa/alsa-util.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index c03866cc..b2736252 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -1289,8 +1289,10 @@ int pa_alsa_find_mixer_and_elem( } if (!e) { - if (ctl_device) + if (ctl_device) { pa_xfree(*ctl_device); + *ctl_device = NULL; + } snd_mixer_close(m); return -1; -- cgit From 0b479ffbbad54b8baa702d1742ebcd9eea2895ec Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:00:51 +0200 Subject: daemon: write a warning blurb to syslog when folks use --system mode --- src/daemon/main.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/daemon/main.c b/src/daemon/main.c index 58f8d660..8058e122 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -930,6 +930,11 @@ int main(int argc, char *argv[]) { pa_log_info(_("Running in system mode: %s"), pa_yes_no(pa_in_system_mode())); + if (pa_in_system_mode()) + pa_log_warn(_("OK, so you are running PA in system mode. Please note that you most likely shouldn't be doing that.\n" + "If you do it nonetheless then it's your own fault if things don't work as expected.\n" + "Please read http://pulseaudio.org/wiki/WhatIsWrongWithSystemMode for an explanation why system mode is usually a bad idea.")); + if (conf->use_pid_file) { int z; -- cgit From 277e8c5ce4cf055108bc8ba17d079a7318932fd2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:01:40 +0200 Subject: idxset: implement pa_idxset_copy() --- src/pulsecore/idxset.c | 14 ++++++++++++++ src/pulsecore/idxset.h | 3 +++ 2 files changed, 17 insertions(+) (limited to 'src') diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c index 352ac977..408011f6 100644 --- a/src/pulsecore/idxset.c +++ b/src/pulsecore/idxset.c @@ -453,3 +453,17 @@ pa_bool_t pa_idxset_isempty(pa_idxset *s) { return s->n_entries == 0; } + +pa_idxset *pa_idxset_copy(pa_idxset *s) { + pa_idxset *copy; + struct idxset_entry *i; + + pa_assert(s); + + copy = pa_idxset_new(s->hash_func, s->compare_func); + + for (i = s->iterate_list_head; i; i = i->iterate_next) + pa_idxset_put(copy, i->data, NULL); + + return copy; +} diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h index a6179fcf..d1e68c5c 100644 --- a/src/pulsecore/idxset.h +++ b/src/pulsecore/idxset.h @@ -103,6 +103,9 @@ unsigned pa_idxset_size(pa_idxset*s); /* Return TRUE of the idxset is empty */ pa_bool_t pa_idxset_isempty(pa_idxset *s); +/* Duplicate the idxset. This will not copy the actual indexes */ +pa_idxset *pa_idxset_copy(pa_idxset *s); + /* A macro to ease iteration through all entries */ #define PA_IDXSET_FOREACH(e, s, idx) \ for ((e) = pa_idxset_first((s), &(idx)); (e); (e) = pa_idxset_next((s), &(idx))) -- cgit From a1d84e39359fed52770f9d15650aabc6bcce6910 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:02:19 +0200 Subject: hashmap: implement api to iterate a hashmap backwards --- src/pulsecore/hashmap.c | 33 +++++++++++++++++++++++++++++++++ src/pulsecore/hashmap.h | 10 +++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c index e957c5ba..b549cb1c 100644 --- a/src/pulsecore/hashmap.c +++ b/src/pulsecore/hashmap.c @@ -237,6 +237,39 @@ at_end: return NULL; } +void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void **key) { + struct hashmap_entry *e; + + pa_assert(h); + pa_assert(state); + + if (*state == (void*) -1) + goto at_beginning; + + if (!*state && !h->iterate_list_tail) + goto at_beginning; + + e = *state ? *state : h->iterate_list_tail; + + if (e->iterate_previous) + *state = e->iterate_previous; + else + *state = (void*) -1; + + if (key) + *key = e->key; + + return e->value; + +at_beginning: + *state = (void *) -1; + + if (key) + *key = NULL; + + return NULL; +} + void* pa_hashmap_first(pa_hashmap *h) { pa_assert(h); diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h index 828e2448..f379fe3c 100644 --- a/src/pulsecore/hashmap.h +++ b/src/pulsecore/hashmap.h @@ -26,7 +26,8 @@ /* Simple Implementation of a hash table. Memory management is the * user's job. It's a good idea to have the key pointer point to a - * string in the value data. */ + * string in the value data. The insertion order is preserved when + * iterating. */ typedef struct pa_hashmap pa_hashmap; @@ -59,6 +60,9 @@ pa_bool_t pa_hashmap_isempty(pa_hashmap *h); returned. */ void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key); +/* Same as pa_hashmap_iterate() but goes backwards */ +void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void**key); + /* Remove the oldest entry in the hashmap and return it */ void *pa_hashmap_steal_first(pa_hashmap *h); @@ -69,4 +73,8 @@ void* pa_hashmap_first(pa_hashmap *h); #define PA_HASHMAP_FOREACH(e, h, state) \ for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL)) +/* A macro to ease iteration through all entries, backwards */ +#define PA_HASHMAP_FOREACH_BACKWARDS(e, h, state) \ + for ((state) = NULL, (e) = pa_hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = pa_hashmap_iterate_backwards((h), &(state), NULL)) + #endif -- cgit From c6830bd9dc53ee745ac331c4ab1c55134562d114 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:02:34 +0200 Subject: hashmap: implement pa_hashmap_last() --- src/pulsecore/hashmap.c | 9 +++++++++ src/pulsecore/hashmap.h | 3 +++ 2 files changed, 12 insertions(+) (limited to 'src') diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c index b549cb1c..1fac97eb 100644 --- a/src/pulsecore/hashmap.c +++ b/src/pulsecore/hashmap.c @@ -279,6 +279,15 @@ void* pa_hashmap_first(pa_hashmap *h) { return h->iterate_list_head->value; } +void* pa_hashmap_last(pa_hashmap *h) { + pa_assert(h); + + if (!h->iterate_list_tail) + return NULL; + + return h->iterate_list_tail->value; +} + void* pa_hashmap_steal_first(pa_hashmap *h) { void *data; diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h index f379fe3c..ac2092a6 100644 --- a/src/pulsecore/hashmap.h +++ b/src/pulsecore/hashmap.h @@ -69,6 +69,9 @@ void *pa_hashmap_steal_first(pa_hashmap *h); /* Return the oldest entry in the hashmap */ void* pa_hashmap_first(pa_hashmap *h); +/* Return the newest entry in the hashmap */ +void* pa_hashmap_last(pa_hashmap *h); + /* A macro to ease iteration through all entries */ #define PA_HASHMAP_FOREACH(e, h, state) \ for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL)) -- cgit From 64b0f38b67ed221ac11d017bd27aa62c6b1a8c2b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:04:21 +0200 Subject: volume: implement functions for multiplicating a cvolume with a scalar --- src/map-file | 2 ++ src/pulse/volume.c | 36 ++++++++++++++++++++++++++++++++++-- src/pulse/volume.h | 12 +++++++++++- 3 files changed, 47 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/map-file b/src/map-file index c46c6792..08fb827d 100644 --- a/src/map-file +++ b/src/map-file @@ -264,7 +264,9 @@ pa_stream_writable_size; pa_stream_write; pa_strerror; pa_sw_cvolume_divide; +pa_sw_cvolume_divide_scalar; pa_sw_cvolume_multiply; +pa_sw_cvolume_multiply_scalar; pa_sw_cvolume_snprint_dB; pa_sw_volume_divide; pa_sw_volume_from_dB; diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 64688e0b..76ef7aa5 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -344,7 +344,7 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_return_val_if_fail(pa_cvolume_valid(a), NULL); pa_return_val_if_fail(pa_cvolume_valid(b), NULL); - for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) + for (i = 0; i < a->channels && i < b->channels; i++) dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]); dest->channels = (uint8_t) i; @@ -352,6 +352,22 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const return dest; } +pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + + for (i = 0; i < a->channels; i++) + dest->values[i] = pa_sw_volume_multiply(a->values[i], b); + + dest->channels = (uint8_t) i; + + return dest; +} + pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { unsigned i; @@ -362,7 +378,7 @@ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa pa_return_val_if_fail(pa_cvolume_valid(a), NULL); pa_return_val_if_fail(pa_cvolume_valid(b), NULL); - for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) + for (i = 0; i < a->channels && i < b->channels; i++) dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]); dest->channels = (uint8_t) i; @@ -370,6 +386,22 @@ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa return dest; } +pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + + for (i = 0; i < a->channels; i++) + dest->values[i] = pa_sw_volume_divide(a->values[i], b); + + dest->channels = (uint8_t) i; + + return dest; +} + int pa_cvolume_valid(const pa_cvolume *v) { unsigned c; diff --git a/src/pulse/volume.h b/src/pulse/volume.h index c07fd99a..05b7ebb4 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -216,16 +216,26 @@ pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; * *dest. This is only valid for software volumes! */ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); +/** Multiply a per-channel volume with a scalar volume and return the + * result in *dest. This is only valid for software volumes! \since + * 0.9.16 */ +pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b); + /** Divide two volume specifications, return the result. This uses * PA_VOLUME_NORM as neutral element of division. This is only valid * for software volumes! If a division by zero is tried the result * will be 0. \since 0.9.13 */ pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; -/** Multiply to per-channel volumes and return the result in +/** Divide two per-channel volumes and return the result in * *dest. This is only valid for software volumes! \since 0.9.13 */ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); +/** Divide a per-channel volume by a scalar volume and return the + * result in *dest. This is only valid for software volumes! \since + * 0.9.16 */ +pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b); + /** Convert a decibel value to a volume (amplitude, not power). This is only valid for software volumes! */ pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST; -- cgit From d9939690ed121931e17e985afe01149da93ca3f3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:05:30 +0200 Subject: channelmap: implement pa_channel_position_from_string() --- src/map-file | 1 + src/pulse/channelmap.c | 48 ++++++++++++++++++++++++++---------------------- src/pulse/channelmap.h | 3 +++ 3 files changed, 30 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/map-file b/src/map-file index 08fb827d..6f8946c6 100644 --- a/src/map-file +++ b/src/map-file @@ -28,6 +28,7 @@ pa_channel_map_superset; pa_channel_map_to_name; pa_channel_map_to_pretty_name; pa_channel_map_valid; +pa_channel_position_from_string; pa_channel_position_to_pretty_string; pa_channel_position_to_string; pa_context_add_autoload; diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index 4654a9ad..f663f176 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -491,6 +491,27 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { return s; } +pa_channel_position_t pa_channel_position_from_string(const char *p) { + pa_channel_position_t i; + pa_assert(p); + + /* Some special aliases */ + if (pa_streq(p, "left")) + return PA_CHANNEL_POSITION_LEFT; + else if (pa_streq(p, "right")) + return PA_CHANNEL_POSITION_RIGHT; + else if (pa_streq(p, "center")) + return PA_CHANNEL_POSITION_CENTER; + else if (pa_streq(p, "subwoofer")) + return PA_CHANNEL_POSITION_SUBWOOFER; + + for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++) + if (pa_streq(p, table[i])) + return i; + + return PA_CHANNEL_POSITION_INVALID; +} + pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { const char *state; pa_channel_map map; @@ -559,36 +580,19 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { map.channels = 0; while ((p = pa_split(s, ",", &state))) { + pa_channel_position_t f; if (map.channels >= PA_CHANNELS_MAX) { pa_xfree(p); return NULL; } - /* Some special aliases */ - if (pa_streq(p, "left")) - map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT; - else if (pa_streq(p, "right")) - map.map[map.channels++] = PA_CHANNEL_POSITION_RIGHT; - else if (pa_streq(p, "center")) - map.map[map.channels++] = PA_CHANNEL_POSITION_CENTER; - else if (pa_streq(p, "subwoofer")) - map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER; - else { - pa_channel_position_t i; - - for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++) - if (strcmp(p, table[i]) == 0) { - map.map[map.channels++] = i; - break; - } - - if (i >= PA_CHANNEL_POSITION_MAX) { - pa_xfree(p); - return NULL; - } + if ((f = pa_channel_position_from_string(p)) == PA_CHANNEL_POSITION_INVALID) { + pa_xfree(p); + return NULL; } + map.map[map.channels++] = f; pa_xfree(p); } diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index 2aaead01..d0474262 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -282,6 +282,9 @@ pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, /** Return a text label for the specified channel position */ const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE; +/* The inverse of pa_channel_position_to_string(). \since 0.9.16 */ +pa_channel_position_t pa_channel_position_from_string(const char *s) PA_GCC_PURE; + /** Return a human readable text label for the specified channel position. \since 0.9.7 */ const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos); -- cgit From 26d5f28f8a53b9b11280eeda42de80d7e06072e9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:06:13 +0200 Subject: version: fix prefix in PA_CHECK_VERSION macro --- src/pulse/version.h.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in index 3143e98e..c2c1f20a 100644 --- a/src/pulse/version.h.in +++ b/src/pulse/version.h.in @@ -64,8 +64,8 @@ const char* pa_get_library_version(void); * newer than the specified. \since 0.9.16 */ #define PA_CHECK_VERSION(major,minor,micro) \ ((PA_MAJOR > (major)) || \ - (PA_MAJOR == (major) && CA_MINOR > (minor)) || \ - (PA_MAJOR == (major) && CA_MINOR == (minor) && CA_MICRO >= (micro))) + (PA_MAJOR == (major) && PA_MINOR > (minor)) || \ + (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro))) PA_C_DECL_END -- cgit From 4f36cc76f25632212d1c661bd82780b2f938819d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:06:54 +0200 Subject: channelmap: make sure a mask is generated is 64 bit int --- src/pulse/channelmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index d0474262..d7901ac2 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -209,7 +209,7 @@ typedef enum pa_channel_position { typedef uint64_t pa_channel_position_mask_t; /** Makes a bit mask from a channel position. \since 0.9.16 */ -#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1 << (f))) +#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1ULL << (f))) /** A list of channel mapping definitions for pa_channel_map_init_auto() */ typedef enum pa_channel_map_def { -- cgit From 697b8de96fb988b7e7de24a549c4e77371a80847 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:07:42 +0200 Subject: malloc: implement pa_xrenew() --- src/pulse/xmalloc.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h index db20496f..f720d83f 100644 --- a/src/pulse/xmalloc.h +++ b/src/pulse/xmalloc.h @@ -88,9 +88,20 @@ static inline void* _pa_xnewdup_internal(const void *p, size_t n, size_t k) { return pa_xmemdup(p, n*k); } -/** Same as pa_xnew() but set the memory to zero */ +/** Same as pa_xnew() but duplicate the specified data */ #define pa_xnewdup(type, p, n) ((type*) _pa_xnewdup_internal((p), (n), sizeof(type))) +/** Internal helper for pa_xrenew() */ +static void* _pa_xrenew_internal(void *p, size_t n, size_t k) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE2(2,3); + +static inline void* _pa_xrenew_internal(void *p, size_t n, size_t k) { + assert(n < INT_MAX/k); + return pa_xrealloc(p, n*k); +} + +/** Reallocate n new structures of the specified type. */ +#define pa_xrenew(type, p, n) ((type*) _pa_xrenew_internal(p, (n), sizeof(type))) + PA_C_DECL_END #endif -- cgit From 77901e5e624dda97c2dcd2c921a14b77d927ff7e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:08:34 +0200 Subject: channelmap: define a couple of standard channel masks --- src/pulsecore/sample-util.h | 59 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'src') diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h index 79af9efc..6a306c11 100644 --- a/src/pulsecore/sample-util.h +++ b/src/pulsecore/sample-util.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -85,4 +86,62 @@ void pa_memchunk_dump_to_file(pa_memchunk *c, const char *fn); void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq); +#define PA_CHANNEL_POSITION_MASK_LEFT \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)) \ + +#define PA_CHANNEL_POSITION_MASK_RIGHT \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT)) + +#define PA_CHANNEL_POSITION_MASK_CENTER \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER)) + +#define PA_CHANNEL_POSITION_MASK_FRONT \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER)) + +#define PA_CHANNEL_POSITION_MASK_REAR \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER)) + +#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER)) + +#define PA_CHANNEL_POSITION_MASK_TOP \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER)) + +#define PA_CHANNEL_POSITION_MASK_ALL \ + ((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1)) + #endif -- cgit From 083b17b28ac986162fe3322270f9169113a160ba Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:09:28 +0200 Subject: volume,channelmap: reimplement a couple of calls based on channel masks --- src/pulse/channelmap.c | 93 +++++++++----------------------------------------- src/pulse/volume.c | 52 +++++----------------------- 2 files changed, 24 insertions(+), 121 deletions(-) (limited to 'src') diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index f663f176..88823012 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -30,9 +30,11 @@ #include #include + #include #include #include +#include #include "channelmap.h" @@ -631,8 +633,7 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s } int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) { - pa_bitset_t in_a[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)]; - unsigned i; + pa_channel_position_mask_t am, bm; pa_assert(a); pa_assert(b); @@ -640,98 +641,36 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) { pa_return_val_if_fail(pa_channel_map_valid(a), 0); pa_return_val_if_fail(pa_channel_map_valid(b), 0); - memset(in_a, 0, sizeof(in_a)); - - for (i = 0; i < a->channels; i++) - pa_bitset_set(in_a, a->map[i], TRUE); + am = pa_channel_map_mask(a); + bm = pa_channel_map_mask(b); - for (i = 0; i < b->channels; i++) - if (!pa_bitset_get(in_a, b->map[i])) - return 0; - - return 1; + return (bm & am) == bm; } int pa_channel_map_can_balance(const pa_channel_map *map) { - unsigned c; - pa_bool_t left = FALSE, right = FALSE; + pa_channel_position_mask_t m; pa_assert(map); - pa_return_val_if_fail(pa_channel_map_valid(map), 0); - for (c = 0; c < map->channels; c++) { - - switch (map->map[c]) { - case PA_CHANNEL_POSITION_LEFT: - case PA_CHANNEL_POSITION_REAR_LEFT: - case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: - case PA_CHANNEL_POSITION_SIDE_LEFT: - case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: - case PA_CHANNEL_POSITION_TOP_REAR_LEFT: - left = TRUE; - break; - - case PA_CHANNEL_POSITION_RIGHT: - case PA_CHANNEL_POSITION_REAR_RIGHT: - case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: - case PA_CHANNEL_POSITION_SIDE_RIGHT: - case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: - case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: - right = TRUE; - break; - - default: - ; - } + m = pa_channel_map_mask(map); - if (left && right) - return 1; - } - - return 0; + return + (PA_CHANNEL_POSITION_MASK_LEFT & m) && + (PA_CHANNEL_POSITION_MASK_RIGHT & m); } int pa_channel_map_can_fade(const pa_channel_map *map) { - unsigned c; - pa_bool_t front = FALSE, rear = FALSE; + pa_channel_position_mask_t m; pa_assert(map); - pa_return_val_if_fail(pa_channel_map_valid(map), 0); - for (c = 0; c < map->channels; c++) { - - switch (map->map[c]) { - case PA_CHANNEL_POSITION_FRONT_LEFT: - case PA_CHANNEL_POSITION_FRONT_RIGHT: - case PA_CHANNEL_POSITION_FRONT_CENTER: - case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: - case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: - case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: - case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: - case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: - front = TRUE; - break; - - case PA_CHANNEL_POSITION_REAR_LEFT: - case PA_CHANNEL_POSITION_REAR_RIGHT: - case PA_CHANNEL_POSITION_REAR_CENTER: - case PA_CHANNEL_POSITION_TOP_REAR_LEFT: - case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: - case PA_CHANNEL_POSITION_TOP_REAR_CENTER: - rear = TRUE; - break; - - default: - ; - } + m = pa_channel_map_mask(map); - if (front && rear) - return 1; - } - - return 0; + return + (PA_CHANNEL_POSITION_MASK_FRONT & m) && + (PA_CHANNEL_POSITION_MASK_REAR & m); } const char* pa_channel_map_to_name(const pa_channel_map *map) { diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 76ef7aa5..42cde5b9 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -27,8 +27,10 @@ #include #include + #include #include +#include #include "volume.h" @@ -418,65 +420,27 @@ int pa_cvolume_valid(const pa_cvolume *v) { } static pa_bool_t on_left(pa_channel_position_t p) { - - return - p == PA_CHANNEL_POSITION_FRONT_LEFT || - p == PA_CHANNEL_POSITION_REAR_LEFT || - p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER || - p == PA_CHANNEL_POSITION_SIDE_LEFT || - p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT || - p == PA_CHANNEL_POSITION_TOP_REAR_LEFT; + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LEFT); } static pa_bool_t on_right(pa_channel_position_t p) { - - return - p == PA_CHANNEL_POSITION_FRONT_RIGHT || - p == PA_CHANNEL_POSITION_REAR_RIGHT || - p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER || - p == PA_CHANNEL_POSITION_SIDE_RIGHT || - p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT || - p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT; + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_RIGHT); } static pa_bool_t on_center(pa_channel_position_t p) { - - return - p == PA_CHANNEL_POSITION_FRONT_CENTER || - p == PA_CHANNEL_POSITION_REAR_CENTER || - p == PA_CHANNEL_POSITION_TOP_CENTER || - p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER || - p == PA_CHANNEL_POSITION_TOP_REAR_CENTER; + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER); } static pa_bool_t on_lfe(pa_channel_position_t p) { - - return - p == PA_CHANNEL_POSITION_LFE; + return p == PA_CHANNEL_POSITION_LFE; } static pa_bool_t on_front(pa_channel_position_t p) { - - return - p == PA_CHANNEL_POSITION_FRONT_LEFT || - p == PA_CHANNEL_POSITION_FRONT_RIGHT || - p == PA_CHANNEL_POSITION_FRONT_CENTER || - p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER || - p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER || - p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT || - p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT || - p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER; + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_FRONT); } static pa_bool_t on_rear(pa_channel_position_t p) { - - return - p == PA_CHANNEL_POSITION_REAR_LEFT || - p == PA_CHANNEL_POSITION_REAR_RIGHT || - p == PA_CHANNEL_POSITION_REAR_CENTER || - p == PA_CHANNEL_POSITION_TOP_REAR_LEFT || - p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT || - p == PA_CHANNEL_POSITION_TOP_REAR_CENTER; + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR); } pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) { -- cgit From 7de7b012fd991100abcd031ed06005911cfea8cd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:11:47 +0200 Subject: conf-parse: implement .include directive --- src/pulsecore/conf-parser.c | 50 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c index a6eb581c..2dc9a223 100644 --- a/src/pulsecore/conf-parser.c +++ b/src/pulsecore/conf-parser.c @@ -40,19 +40,35 @@ #define COMMENTS "#;\n" /* Run the user supplied parser for an assignment */ -static int next_assignment(const char *filename, unsigned line, const char *section, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) { +static int next_assignment( + const char *filename, + unsigned line, + const char *section, + const pa_config_item *t, + const char *lvalue, + const char *rvalue, + void *userdata) { + pa_assert(filename); pa_assert(t); pa_assert(lvalue); pa_assert(rvalue); - for (; t->parse; t++) - if (!t->lvalue || - (pa_streq(lvalue, t->lvalue) && - ((!section && !t->section) || pa_streq(section, t->section)))) - return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata); + for (; t->parse; t++) { + + if (t->lvalue && !pa_streq(lvalue, t->lvalue)) + continue; + + if (t->section && !section) + continue; + + if (t->section && !pa_streq(section, t->section)) + continue; + + return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata); + } - pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, pa_strnull(section)); + pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, pa_strna(section)); return -1; } @@ -96,6 +112,25 @@ static int parse_line(const char *filename, unsigned line, char **section, const if (!*b) return 0; + if (pa_startswith(b, ".include ")) { + char *path, *fn; + int r; + + fn = strip(b+9); + if (!pa_is_path_absolute(fn)) { + const char *k; + if ((k = strrchr(filename, '/'))) { + char *dir = pa_xstrndup(filename, k-filename); + fn = path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir, fn); + pa_xfree(dir); + } + } + + r = pa_config_parse(fn, NULL, t, userdata); + pa_xfree(path); + return r; + } + if (*b == '[') { size_t k; @@ -135,6 +170,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void if (!f && !(f = fopen(filename, "r"))) { if (errno == ENOENT) { + pa_log_debug("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno)); r = 0; goto finish; } -- cgit From c5dbf754b578d70d5bf01494fedad74c1829ac38 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:13:01 +0200 Subject: core-util: implement pa_xstrfreev() --- src/pulsecore/core-util.c | 12 ++++++++++++ src/pulsecore/core-util.h | 6 ++++++ 2 files changed, 18 insertions(+) (limited to 'src') diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index b747cd84..0b64edba 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -2732,3 +2732,15 @@ void pa_disable_sigpipe(void) { } #endif } + +void pa_xfreev(void**a) { + void **p; + + if (!a) + return; + + for (p = a; *p; p++) + pa_xfree(*p); + + pa_xfree(a); +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index d073b750..5a12ad3f 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -229,4 +229,10 @@ char *pa_realpath(const char *path); void pa_disable_sigpipe(void); +void pa_xfreev(void**a); + +static inline void pa_xstrfreev(char **a) { + pa_xfreev((void**) a); +} + #endif -- cgit From 7fa05bea7e9980243cf58902b9d42e995d1a18bf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:13:32 +0200 Subject: core-util: implement pa_split_spaces_strv() --- src/pulsecore/core-util.c | 24 ++++++++++++++++++++++++ src/pulsecore/core-util.h | 2 ++ 2 files changed, 26 insertions(+) (limited to 'src') diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 0b64edba..e39adb12 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -2744,3 +2744,27 @@ void pa_xfreev(void**a) { pa_xfree(a); } + +char **pa_split_spaces_strv(const char *s) { + char **t, *e; + unsigned i = 0, n = 8; + const char *state = NULL; + + t = pa_xnew(char*, n); + while ((e = pa_split_spaces(s, &state))) { + t[i++] = e; + + if (i >= n) { + n *= 2; + t = pa_xrenew(char*, t, n); + } + } + + if (i <= 0) { + pa_xfree(t); + return NULL; + } + + t[i] = NULL; + return t; +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index 5a12ad3f..d88b7cbb 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -235,4 +235,6 @@ static inline void pa_xstrfreev(char **a) { pa_xfreev((void**) a); } +char **pa_split_spaces_strv(const char *s); + #endif -- cgit From 0fa1ddf8380d6b86bd7e911ac6db7771dcb14dd6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:13:59 +0200 Subject: core-util: implement pa_maybe_prefix_path() --- src/pulsecore/core-util.c | 9 +++++++++ src/pulsecore/core-util.h | 2 ++ 2 files changed, 11 insertions(+) (limited to 'src') diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index e39adb12..a71ba0b0 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -2768,3 +2768,12 @@ char **pa_split_spaces_strv(const char *s) { t[i] = NULL; return t; } + +char* pa_maybe_prefix_path(const char *path, const char *prefix) { + pa_assert(path); + + if (pa_is_path_absolute(path)) + return pa_xstrdup(path); + + return pa_sprintf_malloc("%s" PA_PATH_SEP "%s", prefix, path); +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index d88b7cbb..b841edbb 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -237,4 +237,6 @@ static inline void pa_xstrfreev(char **a) { char **pa_split_spaces_strv(const char *s); +char* pa_maybe_prefix_path(const char *path, const char *prefix); + #endif -- cgit From dda0f5a71ac669a304dd73c7de5cb0bc6ee7a75d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:14:26 +0200 Subject: rtp: fix s/recieve/receive/ typo --- src/modules/rtp/module-rtp-recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index c61d2d8b..b86923fb 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -62,7 +62,7 @@ #include "sap.h" PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("Recieve data from a network via RTP/SAP/SDP"); +PA_MODULE_DESCRIPTION("Receive data from a network via RTP/SAP/SDP"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( -- cgit From 325c01bdbc00ad3fcec3982164b100a0bc382109 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:15:36 +0200 Subject: card: some modernizations --- src/pulsecore/card.c | 16 +++++++--------- src/pulsecore/card.h | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c index 9c16ef2d..5a4f01bd 100644 --- a/src/pulsecore/card.c +++ b/src/pulsecore/card.c @@ -148,15 +148,12 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) { c->save_profile = data->save_profile; if (!c->active_profile && c->profiles) { - void *state = NULL; + void *state; pa_card_profile *p; - while ((p = pa_hashmap_iterate(c->profiles, &state, NULL))) { - if (!c->active_profile || - p->priority > c->active_profile->priority) - + PA_HASHMAP_FOREACH(p, c->profiles, state) + if (!c->active_profile || p->priority > c->active_profile->priority) c->active_profile = p; - } } c->userdata = NULL; @@ -177,7 +174,6 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) { void pa_card_free(pa_card *c) { pa_core *core; - pa_card_profile *profile; pa_assert(c); pa_assert(c->core); @@ -200,8 +196,10 @@ void pa_card_free(pa_card *c) { pa_idxset_free(c->sources, NULL, NULL); if (c->profiles) { - while ((profile = pa_hashmap_steal_first(c->profiles))) - pa_card_profile_free(profile); + pa_card_profile *p; + + while ((p = pa_hashmap_steal_first(c->profiles))) + pa_card_profile_free(p); pa_hashmap_free(c->profiles, NULL, NULL); } diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h index 415ab678..aacb24da 100644 --- a/src/pulsecore/card.h +++ b/src/pulsecore/card.h @@ -63,7 +63,7 @@ struct pa_card { pa_hashmap *profiles; pa_card_profile *active_profile; - pa_bool_t save_profile; + pa_bool_t save_profile:1; void *userdata; -- cgit From 4f44fe86fb87076081149b2a2af9392d7fea7c8d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:15:56 +0200 Subject: card: make sure to always hand failure code back in some calls --- src/pulsecore/card.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c index 5a4f01bd..2f0a3af0 100644 --- a/src/pulsecore/card.c +++ b/src/pulsecore/card.c @@ -212,26 +212,27 @@ void pa_card_free(pa_card *c) { int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save) { pa_card_profile *profile; + int r; pa_assert(c); if (!c->set_profile) { - pa_log_warn("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name); - return -1; + pa_log_debug("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name); + return -PA_ERR_NOTIMPLEMENTED; } if (!c->profiles) - return -1; + return -PA_ERR_NOENTITY; if (!(profile = pa_hashmap_get(c->profiles, name))) - return -1; + return -PA_ERR_NOENTITY; if (c->active_profile == profile) { c->save_profile = c->save_profile || save; return 0; } - if (c->set_profile(c, profile) < 0) - return -1; + if ((r = c->set_profile(c, profile)) < 0) + return r; pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); @@ -252,11 +253,19 @@ int pa_card_suspend(pa_card *c, pa_bool_t suspend, pa_suspend_cause_t cause) { pa_assert(c); pa_assert(cause != 0); - for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) - ret -= pa_sink_suspend(sink, suspend, cause) < 0; + for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { + int r; - for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) - ret -= pa_source_suspend(source, suspend, cause) < 0; + if ((r = pa_sink_suspend(sink, suspend, cause)) < 0) + ret = r; + } + + for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { + int r; + + if ((r = pa_source_suspend(source, suspend, cause)) < 0) + ret = r; + } return ret; } -- cgit From 279e0d678e15124f30f3c1a737ddadac1102f735 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:16:13 +0200 Subject: card: get rid of description field which is unused --- src/pulsecore/card.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h index aacb24da..2d691b67 100644 --- a/src/pulsecore/card.h +++ b/src/pulsecore/card.h @@ -72,9 +72,8 @@ struct pa_card { typedef struct pa_card_new_data { char *name; - char *description; - pa_proplist *proplist; + const char *driver; pa_module *module; -- cgit From dddb4b02b390cc7d450aa4c198bf2887b01f035e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:16:48 +0200 Subject: gdbm: set default block size to 1K --- src/pulsecore/database-gdbm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulsecore/database-gdbm.c b/src/pulsecore/database-gdbm.c index aeaac64b..e65125d3 100644 --- a/src/pulsecore/database-gdbm.c +++ b/src/pulsecore/database-gdbm.c @@ -71,10 +71,13 @@ pa_database* pa_database_open(const char *fn, pa_bool_t for_write) { /* We include the host identifier in the file name because gdbm * files are CPU dependant, and we don't want things to go wrong * if we are on a multiarch system. */ - path = pa_sprintf_malloc("%s."CANONICAL_HOST".gdbm", fn); errno = 0; - f = gdbm_open((char*) path, 0, GDBM_NOLOCK | (for_write ? GDBM_WRCREAT : GDBM_READER), 0644, NULL); + + /* We need to set the block size explicitly here, since otherwise + * gdbm takes the native block size of the underlying file system + * which might be incredibly large. */ + f = gdbm_open((char*) path, 1024, GDBM_NOLOCK | (for_write ? GDBM_WRCREAT : GDBM_READER), 0644, NULL); if (f) pa_log_debug("Opened GDBM database '%s'", path); -- cgit From 1ec33f37d9bc9d3328862d2cd9e155d807888a14 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:17:23 +0200 Subject: pstream: fix s/recieve/receive/ typos --- src/pulsecore/pstream.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c index ef1105ba..1d4ac177 100644 --- a/src/pulsecore/pstream.c +++ b/src/pulsecore/pstream.c @@ -684,7 +684,7 @@ static int do_read(pa_pstream *p) { flags = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]); if (!p->use_shm && (flags & PA_FLAG_SHMMASK) != 0) { - pa_log_warn("Recieved SHM frame on a socket where SHM is disabled."); + pa_log_warn("Received SHM frame on a socket where SHM is disabled."); return -1; } @@ -714,7 +714,7 @@ static int do_read(pa_pstream *p) { length = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]); if (length > FRAME_SIZE_MAX_ALLOW || length <= 0) { - pa_log_warn("Recieved invalid frame size: %lu", (unsigned long) length); + pa_log_warn("Received invalid frame size: %lu", (unsigned long) length); return -1; } @@ -743,7 +743,7 @@ static int do_read(pa_pstream *p) { if ((flags & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA) { if (length != sizeof(p->read.shm_info)) { - pa_log_warn("Recieved SHM memblock frame with Invalid frame length."); + pa_log_warn("Received SHM memblock frame with Invalid frame length."); return -1; } @@ -758,7 +758,7 @@ static int do_read(pa_pstream *p) { p->read.data = NULL; } else { - pa_log_warn("Recieved memblock frame with invalid flags value."); + pa_log_warn("Received memblock frame with invalid flags value."); return -1; } } -- cgit From e9c70ac41bb86b7778b67284d8a0cae51f6a9ed3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:18:14 +0200 Subject: pdispatch: fix s/recieve/receive/ typos --- src/pulsecore/pdispatch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c index d00106b4..4388831a 100644 --- a/src/pulsecore/pdispatch.c +++ b/src/pulsecore/pdispatch.c @@ -304,7 +304,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, if (command >= PA_COMMAND_MAX || !(p = command_names[command])) pa_snprintf((char*) (p = t), sizeof(t), "%u", command); - pa_log("[%p] Recieved opcode <%s>", pd, p); + pa_log("[%p] Received opcode <%s>", pd, p); } #endif @@ -325,7 +325,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, (*c)(pd, command, tag, ts, userdata); } else { - pa_log("Recieved unsupported command %u", command); + pa_log("Received unsupported command %u", command); goto finish; } -- cgit From 31575f7766d6ff39665b64a3a04412eff1c39957 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 03:45:14 +0200 Subject: alsa: rework mixer logic Completely rework mixer logic. This now allows controlling a full set of elements from a single sink's volume slider/mute button. This also introduces sink and source "ports" that can be used to choose different input or output ports with the UI. (i.e. "mic"/"line-in" or "speaker"/"headphones". The mixer paths and device maps are now configered in external configuration files and can be tweaked as necessary. --- src/Makefile.am | 47 +- src/modules/alsa/alsa-mixer.c | 3382 ++++++++++++++++++++ src/modules/alsa/alsa-mixer.h | 292 ++ src/modules/alsa/alsa-sink.c | 474 ++- src/modules/alsa/alsa-sink.h | 3 +- src/modules/alsa/alsa-source.c | 476 ++- src/modules/alsa/alsa-source.h | 2 +- src/modules/alsa/alsa-util.c | 1036 +----- src/modules/alsa/alsa-util.h | 116 +- src/modules/alsa/mixer/paths/analog-input-aux.conf | 32 + src/modules/alsa/mixer/paths/analog-input-fm.conf | 44 + .../alsa/mixer/paths/analog-input-linein.conf | 43 + .../alsa/mixer/paths/analog-input-mic-line.conf | 44 + src/modules/alsa/mixer/paths/analog-input-mic.conf | 45 + .../alsa/mixer/paths/analog-input-mic.conf.common | 41 + .../alsa/mixer/paths/analog-input-tvtuner.conf | 44 + .../alsa/mixer/paths/analog-input-video.conf | 31 + src/modules/alsa/mixer/paths/analog-input.conf | 35 + .../alsa/mixer/paths/analog-input.conf.common | 239 ++ .../alsa/mixer/paths/analog-output-headphones.conf | 53 + .../mixer/paths/analog-output-lfe-on-mono.conf | 54 + .../alsa/mixer/paths/analog-output-mono.conf | 51 + src/modules/alsa/mixer/paths/analog-output.conf | 62 + .../alsa/mixer/paths/analog-output.conf.common | 40 + src/modules/alsa/mixer/profile-sets/default.conf | 105 + .../mixer/samples/ATI IXP--Realtek ALC655 rev 0 | 150 + .../alsa/mixer/samples/Brooktree Bt878--Bt87x | 24 + .../Ensoniq AudioPCI--Cirrus Logic CS4297A rev 3 | 135 + .../alsa/mixer/samples/HDA ATI HDMI--ATI R6xx HDMI | 4 + .../mixer/samples/HDA Intel--Analog Devices AD1981 | 62 + .../alsa/mixer/samples/HDA Intel--Realtek ALC889A | 113 + .../Intel 82801CA-ICH3--Analog Devices AD1881A | 128 + .../mixer/samples/Logitech USB Speaker--USB Mixer | 27 + .../alsa/mixer/samples/USB Audio--USB Mixer | 37 + .../samples/USB Device 0x46d:0x9a4--USB Mixer | 5 + .../mixer/samples/VIA 8237--Analog Devices AD1888 | 211 ++ .../VIA 8237--C-Media Electronics CMI9761A+ | 160 + src/modules/alsa/module-alsa-card.c | 261 +- src/modules/bluetooth/module-bluetooth-device.c | 8 +- src/modules/bluetooth/module-bluetooth-proximity.c | 4 +- src/modules/module-ladspa-sink.c | 23 +- src/modules/module-lirc.c | 12 +- src/modules/module-mmkbd-evdev.c | 6 +- src/modules/module-tunnel.c | 8 +- src/modules/udev-util.c | 40 +- src/modules/udev-util.h | 3 +- src/pulsecore/cli-command.c | 78 +- src/pulsecore/cli-text.c | 36 +- src/pulsecore/protocol-native.c | 8 +- src/pulsecore/sink-input.c | 10 +- src/pulsecore/sink.c | 144 +- src/pulsecore/sink.h | 46 +- src/pulsecore/source.c | 121 +- src/pulsecore/source.h | 32 +- 54 files changed, 7011 insertions(+), 1676 deletions(-) create mode 100644 src/modules/alsa/alsa-mixer.c create mode 100644 src/modules/alsa/alsa-mixer.h create mode 100644 src/modules/alsa/mixer/paths/analog-input-aux.conf create mode 100644 src/modules/alsa/mixer/paths/analog-input-fm.conf create mode 100644 src/modules/alsa/mixer/paths/analog-input-linein.conf create mode 100644 src/modules/alsa/mixer/paths/analog-input-mic-line.conf create mode 100644 src/modules/alsa/mixer/paths/analog-input-mic.conf create mode 100644 src/modules/alsa/mixer/paths/analog-input-mic.conf.common create mode 100644 src/modules/alsa/mixer/paths/analog-input-tvtuner.conf create mode 100644 src/modules/alsa/mixer/paths/analog-input-video.conf create mode 100644 src/modules/alsa/mixer/paths/analog-input.conf create mode 100644 src/modules/alsa/mixer/paths/analog-input.conf.common create mode 100644 src/modules/alsa/mixer/paths/analog-output-headphones.conf create mode 100644 src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf create mode 100644 src/modules/alsa/mixer/paths/analog-output-mono.conf create mode 100644 src/modules/alsa/mixer/paths/analog-output.conf create mode 100644 src/modules/alsa/mixer/paths/analog-output.conf.common create mode 100644 src/modules/alsa/mixer/profile-sets/default.conf create mode 100644 src/modules/alsa/mixer/samples/ATI IXP--Realtek ALC655 rev 0 create mode 100644 src/modules/alsa/mixer/samples/Brooktree Bt878--Bt87x create mode 100644 src/modules/alsa/mixer/samples/Ensoniq AudioPCI--Cirrus Logic CS4297A rev 3 create mode 100644 src/modules/alsa/mixer/samples/HDA ATI HDMI--ATI R6xx HDMI create mode 100644 src/modules/alsa/mixer/samples/HDA Intel--Analog Devices AD1981 create mode 100644 src/modules/alsa/mixer/samples/HDA Intel--Realtek ALC889A create mode 100644 src/modules/alsa/mixer/samples/Intel 82801CA-ICH3--Analog Devices AD1881A create mode 100644 src/modules/alsa/mixer/samples/Logitech USB Speaker--USB Mixer create mode 100644 src/modules/alsa/mixer/samples/USB Audio--USB Mixer create mode 100644 src/modules/alsa/mixer/samples/USB Device 0x46d:0x9a4--USB Mixer create mode 100644 src/modules/alsa/mixer/samples/VIA 8237--Analog Devices AD1888 create mode 100644 src/modules/alsa/mixer/samples/VIA 8237--C-Media Electronics CMI9761A+ (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 38395e7a..5d8487ae 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,6 +29,8 @@ pulsecoreincludedir=$(includedir)/pulsecore pulseconfdir=$(sysconfdir)/pulse pulselibexecdir=$(libexecdir)/pulse xdgautostartdir=$(sysconfdir)/xdg/autostart +alsaprofilesetsdir=$(datadir)/alsa-mixer/profile-sets +alsapathsdir=$(datadir)/alsa-mixer/paths ################################### # Defines # @@ -73,7 +75,9 @@ AM_CFLAGS = \ -DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\" \ -DAO_REQUIRE_CAS \ -DPULSE_LOCALEDIR=\"$(pulselocaledir)\" \ - -DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\" + -DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\" \ + -DPA_ALSA_PATHS_DIR=\"$(alsapathsdir)\" \ + -DPA_ALSA_PROFILE_SETS_DIR=\"$(alsaprofilesetsdir)\" AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS) AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS) @@ -109,7 +113,23 @@ EXTRA_DIST = \ modules/module-defs.h.m4 \ daemon/pulseaudio.desktop.in \ map-file \ - daemon/org.pulseaudio.policy.in + daemon/org.pulseaudio.policy.in \ + modules/alsa/mixer/profile-sets/default.conf \ + modules/alsa/mixer/paths/analog-input-aux.conf \ + modules/alsa/mixer/paths/analog-input.conf \ + modules/alsa/mixer/paths/analog-input.conf.common \ + modules/alsa/mixer/paths/analog-input-fm.conf \ + modules/alsa/mixer/paths/analog-input-linein.conf \ + modules/alsa/mixer/paths/analog-input-mic.conf \ + modules/alsa/mixer/paths/analog-input-mic.conf.common \ + modules/alsa/mixer/paths/analog-input-mic-line.conf \ + modules/alsa/mixer/paths/analog-input-tvtuner.conf \ + modules/alsa/mixer/paths/analog-input-video.conf \ + modules/alsa/mixer/paths/analog-output.conf \ + modules/alsa/mixer/paths/analog-output.conf.common \ + modules/alsa/mixer/paths/analog-output-headphones.conf \ + modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf \ + modules/alsa/mixer/paths/analog-output-mono.conf pulseconf_DATA = \ default.pa \ @@ -1023,6 +1043,27 @@ modlibexec_LTLIBRARIES += \ module-alsa-sink.la \ module-alsa-source.la \ module-alsa-card.la + +alsaprofilesets_DATA = \ + modules/alsa/mixer/profile-sets/default.conf + +alsapaths_DATA = \ + modules/alsa/mixer/paths/analog-input-aux.conf \ + modules/alsa/mixer/paths/analog-input.conf \ + modules/alsa/mixer/paths/analog-input.conf.common \ + modules/alsa/mixer/paths/analog-input-fm.conf \ + modules/alsa/mixer/paths/analog-input-linein.conf \ + modules/alsa/mixer/paths/analog-input-mic.conf \ + modules/alsa/mixer/paths/analog-input-mic.conf.common \ + modules/alsa/mixer/paths/analog-input-mic-line.conf \ + modules/alsa/mixer/paths/analog-input-tvtuner.conf \ + modules/alsa/mixer/paths/analog-input-video.conf \ + modules/alsa/mixer/paths/analog-output.conf \ + modules/alsa/mixer/paths/analog-output.conf.common \ + modules/alsa/mixer/paths/analog-output-headphones.conf \ + modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf \ + modules/alsa/mixer/paths/analog-output-mono.conf + endif if HAVE_SOLARIS @@ -1346,7 +1387,7 @@ module_oss_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore-@PA_MAJORMINORMI # ALSA -libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h modules/alsa/alsa-source.c modules/alsa/alsa-source.h modules/reserve-wrap.c modules/reserve-wrap.h +libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h modules/alsa/alsa-source.c modules/alsa/alsa-source.h modules/reserve-wrap.c modules/reserve-wrap.h libalsa_util_la_LDFLAGS = -avoid-version libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c new file mode 100644 index 00000000..53875165 --- /dev/null +++ b/src/modules/alsa/alsa-mixer.c @@ -0,0 +1,3382 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2009 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "alsa-mixer.h" +#include "alsa-util.h" + +struct description_map { + const char *name; + const char *description; +}; + +static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) { + unsigned i; + + for (i = 0; i < n; i++) + if (pa_streq(dm[i].name, name)) + return dm[i].description; + + return NULL; +} + +struct pa_alsa_fdlist { + unsigned num_fds; + struct pollfd *fds; + /* This is a temporary buffer used to avoid lots of mallocs */ + struct pollfd *work_fds; + + snd_mixer_t *mixer; + + pa_mainloop_api *m; + pa_defer_event *defer; + pa_io_event **ios; + + pa_bool_t polled; + + void (*cb)(void *userdata); + void *userdata; +}; + +static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { + + struct pa_alsa_fdlist *fdl = userdata; + int err; + unsigned i; + unsigned short revents; + + pa_assert(a); + pa_assert(fdl); + pa_assert(fdl->mixer); + pa_assert(fdl->fds); + pa_assert(fdl->work_fds); + + if (fdl->polled) + return; + + fdl->polled = TRUE; + + memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds); + + for (i = 0; i < fdl->num_fds; i++) { + if (e == fdl->ios[i]) { + if (events & PA_IO_EVENT_INPUT) + fdl->work_fds[i].revents |= POLLIN; + if (events & PA_IO_EVENT_OUTPUT) + fdl->work_fds[i].revents |= POLLOUT; + if (events & PA_IO_EVENT_ERROR) + fdl->work_fds[i].revents |= POLLERR; + if (events & PA_IO_EVENT_HANGUP) + fdl->work_fds[i].revents |= POLLHUP; + break; + } + } + + pa_assert(i != fdl->num_fds); + + if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) { + pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err)); + return; + } + + a->defer_enable(fdl->defer, 1); + + if (revents) + snd_mixer_handle_events(fdl->mixer); +} + +static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) { + struct pa_alsa_fdlist *fdl = userdata; + unsigned num_fds, i; + int err, n; + struct pollfd *temp; + + pa_assert(a); + pa_assert(fdl); + pa_assert(fdl->mixer); + + a->defer_enable(fdl->defer, 0); + + if ((n = snd_mixer_poll_descriptors_count(fdl->mixer)) < 0) { + pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n)); + return; + } + num_fds = (unsigned) n; + + if (num_fds != fdl->num_fds) { + if (fdl->fds) + pa_xfree(fdl->fds); + if (fdl->work_fds) + pa_xfree(fdl->work_fds); + fdl->fds = pa_xnew0(struct pollfd, num_fds); + fdl->work_fds = pa_xnew(struct pollfd, num_fds); + } + + memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds); + + if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) { + pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err)); + return; + } + + fdl->polled = FALSE; + + if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0) + return; + + if (fdl->ios) { + for (i = 0; i < fdl->num_fds; i++) + a->io_free(fdl->ios[i]); + + if (num_fds != fdl->num_fds) { + pa_xfree(fdl->ios); + fdl->ios = NULL; + } + } + + if (!fdl->ios) + fdl->ios = pa_xnew(pa_io_event*, num_fds); + + /* Swap pointers */ + temp = fdl->work_fds; + fdl->work_fds = fdl->fds; + fdl->fds = temp; + + fdl->num_fds = num_fds; + + for (i = 0;i < num_fds;i++) + fdl->ios[i] = a->io_new(a, fdl->fds[i].fd, + ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) | + ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0), + io_cb, fdl); +} + +struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) { + struct pa_alsa_fdlist *fdl; + + fdl = pa_xnew0(struct pa_alsa_fdlist, 1); + + return fdl; +} + +void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) { + pa_assert(fdl); + + if (fdl->defer) { + pa_assert(fdl->m); + fdl->m->defer_free(fdl->defer); + } + + if (fdl->ios) { + unsigned i; + pa_assert(fdl->m); + for (i = 0; i < fdl->num_fds; i++) + fdl->m->io_free(fdl->ios[i]); + pa_xfree(fdl->ios); + } + + if (fdl->fds) + pa_xfree(fdl->fds); + if (fdl->work_fds) + pa_xfree(fdl->work_fds); + + pa_xfree(fdl); +} + +int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) { + pa_assert(fdl); + pa_assert(mixer_handle); + pa_assert(m); + pa_assert(!fdl->m); + + fdl->mixer = mixer_handle; + fdl->m = m; + fdl->defer = m->defer_new(m, defer_cb, fdl); + + return 0; +} + +static int prepare_mixer(snd_mixer_t *mixer, const char *dev) { + int err; + + pa_assert(mixer); + pa_assert(dev); + + if ((err = snd_mixer_attach(mixer, dev)) < 0) { + pa_log_info("Unable to attach to mixer %s: %s", dev, pa_alsa_strerror(err)); + return -1; + } + + if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) { + pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err)); + return -1; + } + + if ((err = snd_mixer_load(mixer)) < 0) { + pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err)); + return -1; + } + + pa_log_info("Successfully attached to mixer '%s'", dev); + return 0; +} + +snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) { + int err; + snd_mixer_t *m; + const char *dev; + snd_pcm_info_t* info; + snd_pcm_info_alloca(&info); + + pa_assert(pcm); + + if ((err = snd_mixer_open(&m, 0)) < 0) { + pa_log("Error opening mixer: %s", pa_alsa_strerror(err)); + return NULL; + } + + /* First, try by name */ + if ((dev = snd_pcm_name(pcm))) + if (prepare_mixer(m, dev) >= 0) { + if (ctl_device) + *ctl_device = pa_xstrdup(dev); + + return m; + } + + /* Then, try by card index */ + if (snd_pcm_info(pcm, info) >= 0) { + char *md; + int card_idx; + + if ((card_idx = snd_pcm_info_get_card(info)) >= 0) { + + md = pa_sprintf_malloc("hw:%i", card_idx); + + if (!dev || !pa_streq(dev, md)) + if (prepare_mixer(m, md) >= 0) { + + if (ctl_device) + *ctl_device = md; + else + pa_xfree(md); + + return m; + } + + pa_xfree(md); + } + } + + snd_mixer_close(m); + return NULL; +} + +static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = { + [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */ + + [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER, + [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT, + [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT, + + [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER, + [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT, + [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT, + + [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER, + + [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN, + + [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT, + [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT, + + [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN, + + [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN, + + [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN, + + [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN, + [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN +}; + +static void setting_free(pa_alsa_setting *s) { + pa_assert(s); + + if (s->options) + pa_idxset_free(s->options, NULL, NULL); + + pa_xfree(s->name); + pa_xfree(s->description); + pa_xfree(s); +} + +static void option_free(pa_alsa_option *o) { + pa_assert(o); + + pa_xfree(o->alsa_name); + pa_xfree(o->name); + pa_xfree(o->description); + pa_xfree(o); +} + +static void element_free(pa_alsa_element *e) { + pa_alsa_option *o; + pa_assert(e); + + while ((o = e->options)) { + PA_LLIST_REMOVE(pa_alsa_option, e->options, o); + option_free(o); + } + + pa_xfree(e->alsa_name); + pa_xfree(e); +} + +void pa_alsa_path_free(pa_alsa_path *p) { + pa_alsa_element *e; + pa_alsa_setting *s; + + pa_assert(p); + + while ((e = p->elements)) { + PA_LLIST_REMOVE(pa_alsa_element, p->elements, e); + element_free(e); + } + + while ((s = p->settings)) { + PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s); + setting_free(s); + } + + pa_xfree(p->name); + pa_xfree(p->description); + pa_xfree(p); +} + +void pa_alsa_path_set_free(pa_alsa_path_set *ps) { + pa_alsa_path *p; + pa_assert(ps); + + while ((p = ps->paths)) { + PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p); + pa_alsa_path_free(p); + } + + pa_xfree(ps); +} + +static long to_alsa_dB(pa_volume_t v) { + return (long) (pa_sw_volume_to_dB(v) * 100.0); +} + +static pa_volume_t from_alsa_dB(long v) { + return pa_sw_volume_from_dB((double) v / 100.0); +} + +static long to_alsa_volume(pa_volume_t v, long min, long max) { + long w; + + w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min; + return PA_CLAMP_UNLIKELY(w, min, max); +} + +static pa_volume_t from_alsa_volume(long v, long min, long max) { + return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min)); +} + +#define SELEM_INIT(sid, name) \ + do { \ + snd_mixer_selem_id_alloca(&(sid)); \ + snd_mixer_selem_id_set_name((sid), (name)); \ + snd_mixer_selem_id_set_index((sid), 0); \ + } while(FALSE) + +static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) { + snd_mixer_selem_id_t *sid; + snd_mixer_elem_t *me; + snd_mixer_selem_channel_id_t c; + pa_channel_position_mask_t mask = 0; + unsigned k; + + pa_assert(m); + pa_assert(e); + pa_assert(cm); + pa_assert(v); + + SELEM_INIT(sid, e->alsa_name); + if (!(me = snd_mixer_find_selem(m, sid))) { + pa_log_warn("Element %s seems to have disappeared.", e->alsa_name); + return -1; + } + + pa_cvolume_mute(v, cm->channels); + + /* We take the highest volume of all channels that match */ + + for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) { + int r; + pa_volume_t f; + + if (e->has_dB) { + long value = 0; + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { + if (snd_mixer_selem_has_playback_channel(me, c)) + r = snd_mixer_selem_get_playback_dB(me, c, &value); + else + r = -1; + } else { + if (snd_mixer_selem_has_capture_channel(me, c)) + r = snd_mixer_selem_get_capture_dB(me, c, &value); + else + r = -1; + } + + if (r < 0) + continue; + +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value)); +#endif + + f = from_alsa_dB(value); + + } else { + long value = 0; + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { + if (snd_mixer_selem_has_playback_channel(me, c)) + r = snd_mixer_selem_get_playback_volume(me, c, &value); + else + r = -1; + } else { + if (snd_mixer_selem_has_capture_channel(me, c)) + r = snd_mixer_selem_get_capture_volume(me, c, &value); + else + r = -1; + } + + if (r < 0) + continue; + + f = from_alsa_volume(value, e->min_volume, e->max_volume); + } + + for (k = 0; k < cm->channels; k++) + if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) + if (v->values[k] < f) + v->values[k] = f; + + mask |= e->masks[c][e->n_channels-1]; + } + + for (k = 0; k < cm->channels; k++) + if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k]))) + v->values[k] = PA_VOLUME_NORM; + + return 0; +} + +int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) { + pa_alsa_element *e; + + pa_assert(m); + pa_assert(p); + pa_assert(cm); + pa_assert(v); + + if (!p->has_volume) + return -1; + + pa_cvolume_reset(v, cm->channels); + + PA_LLIST_FOREACH(e, p->elements) { + pa_cvolume ev; + + if (e->volume_use != PA_ALSA_VOLUME_MERGE) + continue; + + pa_assert(!p->has_dB || e->has_dB); + + if (element_get_volume(e, m, cm, &ev) < 0) + return -1; + + /* If we have no dB information all we can do is take the first element and leave */ + if (!p->has_dB) { + *v = ev; + return 0; + } + + pa_sw_cvolume_multiply(v, v, &ev); + } + + return 0; +} + +static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) { + snd_mixer_selem_id_t *sid; + snd_mixer_elem_t *me; + snd_mixer_selem_channel_id_t c; + + pa_assert(m); + pa_assert(e); + pa_assert(b); + + SELEM_INIT(sid, e->alsa_name); + if (!(me = snd_mixer_find_selem(m, sid))) { + pa_log_warn("Element %s seems to have disappeared.", e->alsa_name); + return -1; + } + + /* We return muted if at least one channel is muted */ + + for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) { + int r; + int value = 0; + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { + if (snd_mixer_selem_has_playback_channel(me, c)) + r = snd_mixer_selem_get_playback_switch(me, c, &value); + else + r = -1; + } else { + if (snd_mixer_selem_has_capture_channel(me, c)) + r = snd_mixer_selem_get_capture_switch(me, c, &value); + else + r = -1; + } + + if (r < 0) + continue; + + if (!value) { + *b = FALSE; + return 0; + } + } + + *b = TRUE; + return 0; +} + +int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) { + pa_alsa_element *e; + + pa_assert(m); + pa_assert(p); + pa_assert(muted); + + if (!p->has_mute) + return -1; + + PA_LLIST_FOREACH(e, p->elements) { + pa_bool_t b; + + if (e->switch_use != PA_ALSA_SWITCH_MUTE) + continue; + + if (element_get_switch(e, m, &b) < 0) + return -1; + + if (!b) { + *muted = TRUE; + return 0; + } + } + + *muted = FALSE; + return 0; +} + +static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) { + snd_mixer_selem_id_t *sid; + pa_cvolume rv; + snd_mixer_elem_t *me; + snd_mixer_selem_channel_id_t c; + pa_channel_position_mask_t mask = 0; + unsigned k; + + pa_assert(m); + pa_assert(e); + pa_assert(cm); + pa_assert(v); + pa_assert(pa_cvolume_compatible_with_channel_map(v, cm)); + + SELEM_INIT(sid, e->alsa_name); + if (!(me = snd_mixer_find_selem(m, sid))) { + pa_log_warn("Element %s seems to have disappeared.", e->alsa_name); + return -1; + } + + pa_cvolume_mute(&rv, cm->channels); + + for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) { + int r; + pa_volume_t f = PA_VOLUME_MUTED; + + for (k = 0; k < cm->channels; k++) + if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) + if (v->values[k] > f) + f = v->values[k]; + + if (e->has_dB) { + long value = to_alsa_dB(f); + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { + /* If we call set_play_volume() without checking first + * if the channel is available, ALSA behaves ver + * strangely and doesn't fail the call */ + if (snd_mixer_selem_has_playback_channel(me, c)) { + if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0) + r = snd_mixer_selem_get_playback_dB(me, c, &value); + } else + r = -1; + } else { + if (snd_mixer_selem_has_capture_channel(me, c)) { + if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0) + r = snd_mixer_selem_get_capture_dB(me, c, &value); + } else + r = -1; + } + + if (r < 0) + continue; + +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value)); +#endif + + f = from_alsa_dB(value); + + } else { + long value; + + value = to_alsa_volume(f, e->min_volume, e->max_volume); + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { + if (snd_mixer_selem_has_playback_channel(me, c)) { + if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0) + r = snd_mixer_selem_get_playback_volume(me, c, &value); + } else + r = -1; + } else { + if (snd_mixer_selem_has_capture_channel(me, c)) { + if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0) + r = snd_mixer_selem_get_capture_volume(me, c, &value); + } else + r = -1; + } + + if (r < 0) + continue; + + f = from_alsa_volume(value, e->min_volume, e->max_volume); + } + + for (k = 0; k < cm->channels; k++) + if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) + if (rv.values[k] < f) + rv.values[k] = f; + + mask |= e->masks[c][e->n_channels-1]; + } + + for (k = 0; k < cm->channels; k++) + if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k]))) + rv.values[k] = PA_VOLUME_NORM; + + *v = rv; + return 0; +} + +int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) { + pa_alsa_element *e; + pa_cvolume rv; + + pa_assert(m); + pa_assert(p); + pa_assert(cm); + pa_assert(v); + pa_assert(pa_cvolume_compatible_with_channel_map(v, cm)); + + if (!p->has_volume) + return -1; + + rv = *v; /* Remaining adjustment */ + pa_cvolume_reset(v, cm->channels); /* Adjustment done */ + + PA_LLIST_FOREACH(e, p->elements) { + pa_cvolume ev; + + if (e->volume_use != PA_ALSA_VOLUME_MERGE) + continue; + + pa_assert(!p->has_dB || e->has_dB); + + ev = rv; + if (element_set_volume(e, m, cm, &ev) < 0) + return -1; + + if (!p->has_dB) { + *v = ev; + return 0; + } + + pa_sw_cvolume_multiply(v, v, &ev); + pa_sw_cvolume_divide(&rv, &rv, &ev); + } + + return 0; +} + +static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) { + snd_mixer_elem_t *me; + snd_mixer_selem_id_t *sid; + int r; + + pa_assert(m); + pa_assert(e); + + SELEM_INIT(sid, e->alsa_name); + if (!(me = snd_mixer_find_selem(m, sid))) { + pa_log_warn("Element %s seems to have disappeared.", e->alsa_name); + return -1; + } + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + r = snd_mixer_selem_set_playback_switch_all(me, b); + else + r = snd_mixer_selem_set_capture_switch_all(me, b); + + if (r < 0) + pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno)); + + return r; +} + +int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) { + pa_alsa_element *e; + + pa_assert(m); + pa_assert(p); + + if (!p->has_mute) + return -1; + + PA_LLIST_FOREACH(e, p->elements) { + + if (e->switch_use != PA_ALSA_SWITCH_MUTE) + continue; + + if (element_set_switch(e, m, !muted) < 0) + return -1; + } + + return 0; +} + +static int element_mute_volume(pa_alsa_element *e, snd_mixer_t *m) { + snd_mixer_elem_t *me; + snd_mixer_selem_id_t *sid; + int r; + + pa_assert(m); + pa_assert(e); + + SELEM_INIT(sid, e->alsa_name); + if (!(me = snd_mixer_find_selem(m, sid))) { + pa_log_warn("Element %s seems to have disappeared.", e->alsa_name); + return -1; + } + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume); + else + r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume); + + if (r < 0) + pa_log_warn("Faile to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno)); + + return r; +} + +/* The volume to 0dB */ +static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) { + snd_mixer_elem_t *me; + snd_mixer_selem_id_t *sid; + int r; + + pa_assert(m); + pa_assert(e); + + SELEM_INIT(sid, e->alsa_name); + if (!(me = snd_mixer_find_selem(m, sid))) { + pa_log_warn("Element %s seems to have disappeared.", e->alsa_name); + return -1; + } + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + r = snd_mixer_selem_set_playback_dB_all(me, 0, +1); + else + r = snd_mixer_selem_set_capture_dB_all(me, 0, +1); + + if (r < 0) + pa_log_warn("Faile to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno)); + + return r; +} + +int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { + pa_alsa_element *e; + int r; + + pa_assert(m); + pa_assert(p); + + pa_log_debug("Activating path %s", p->name); + pa_alsa_path_dump(p); + + PA_LLIST_FOREACH(e, p->elements) { + + switch (e->switch_use) { + case PA_ALSA_SWITCH_MUTE: + case PA_ALSA_SWITCH_OFF: + r = element_set_switch(e, m, FALSE); + break; + + case PA_ALSA_SWITCH_ON: + r = element_set_switch(e, m, TRUE); + break; + + case PA_ALSA_SWITCH_IGNORE: + case PA_ALSA_SWITCH_SELECT: + r = 0; + break; + } + + if (r < 0) + return -1; + + switch (e->volume_use) { + case PA_ALSA_VOLUME_OFF: + case PA_ALSA_VOLUME_MERGE: + r = element_mute_volume(e, m); + break; + + case PA_ALSA_VOLUME_ZERO: + r = element_zero_volume(e, m); + break; + + case PA_ALSA_VOLUME_IGNORE: + r = 0; + break; + } + + if (r < 0) + return -1; + } + + return 0; +} + +static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) { + pa_bool_t has_switch; + pa_bool_t has_enumeration; + pa_bool_t has_volume; + + pa_assert(e); + pa_assert(me); + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { + has_switch = + snd_mixer_selem_has_playback_switch(me) || + (e->direction_try_other && snd_mixer_selem_has_capture_switch(me)); + } else { + has_switch = + snd_mixer_selem_has_capture_switch(me) || + (e->direction_try_other && snd_mixer_selem_has_playback_switch(me)); + } + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { + has_volume = + snd_mixer_selem_has_playback_volume(me) || + (e->direction_try_other && snd_mixer_selem_has_capture_volume(me)); + } else { + has_volume = + snd_mixer_selem_has_capture_volume(me) || + (e->direction_try_other && snd_mixer_selem_has_playback_volume(me)); + } + + has_enumeration = snd_mixer_selem_is_enumerated(me); + + if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) || + (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) || + (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration)) + return -1; + + if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration)) + return -1; + + if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) || + (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) || + (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration)) + return -1; + + if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration)) + return -1; + + return 0; +} + +static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { + snd_mixer_selem_id_t *sid; + snd_mixer_elem_t *me; + + pa_assert(m); + pa_assert(e); + + SELEM_INIT(sid, e->alsa_name); + + if (!(me = snd_mixer_find_selem(m, sid))) { + + if (e->required != PA_ALSA_REQUIRED_IGNORE) + return -1; + + e->switch_use = PA_ALSA_SWITCH_IGNORE; + e->volume_use = PA_ALSA_VOLUME_IGNORE; + e->enumeration_use = PA_ALSA_VOLUME_IGNORE; + + return 0; + } + + if (e->switch_use != PA_ALSA_SWITCH_IGNORE) { + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { + + if (!snd_mixer_selem_has_playback_switch(me)) { + if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me)) + e->direction = PA_ALSA_DIRECTION_INPUT; + else + e->switch_use = PA_ALSA_SWITCH_IGNORE; + } + + } else { + + if (!snd_mixer_selem_has_capture_switch(me)) { + if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me)) + e->direction = PA_ALSA_DIRECTION_OUTPUT; + else + e->switch_use = PA_ALSA_SWITCH_IGNORE; + } + } + + if (e->switch_use != PA_ALSA_SWITCH_IGNORE) + e->direction_try_other = FALSE; + } + + if (e->volume_use != PA_ALSA_VOLUME_IGNORE) { + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { + + if (!snd_mixer_selem_has_playback_volume(me)) { + if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me)) + e->direction = PA_ALSA_DIRECTION_INPUT; + else + e->volume_use = PA_ALSA_VOLUME_IGNORE; + } + + } else { + + if (!snd_mixer_selem_has_capture_volume(me)) { + if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me)) + e->direction = PA_ALSA_DIRECTION_OUTPUT; + else + e->volume_use = PA_ALSA_VOLUME_IGNORE; + } + } + + if (e->volume_use != PA_ALSA_VOLUME_IGNORE) { + long min_dB = 0, max_dB = 0; + int r; + + e->direction_try_other = FALSE; + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0; + else + e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0; + + if (e->has_dB) { +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB)); + VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB)); +#endif + + e->min_dB = ((double) min_dB) / 100.0; + e->max_dB = ((double) max_dB) / 100.0; + + if (min_dB >= max_dB) { + pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e->min_dB, e->max_dB); + e->has_dB = FALSE; + } + } + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume); + else + r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume); + + if (r < 0) { + pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r)); + return -1; + } + + + if (e->min_volume >= e->max_volume) { + pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", e->min_volume, e->max_volume); + e->volume_use = PA_ALSA_VOLUME_IGNORE; + + } else { + pa_bool_t is_mono; + pa_channel_position_t p; + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + is_mono = snd_mixer_selem_is_playback_mono(me) > 0; + else + is_mono = snd_mixer_selem_is_capture_mono(me) > 0; + + if (is_mono) { + e->n_channels = 1; + + if (!e->override_map) { + for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) + e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0; + e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL; + } + + e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1]; + } else { + e->n_channels = 0; + for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) { + + if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN) + continue; + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0; + else + e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0; + } + + if (e->n_channels <= 0) { + pa_log_warn("Volume element %s with no channels?", e->alsa_name); + return -1; + } + + if (!e->override_map) { + for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) { + pa_bool_t has_channel; + + if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN) + continue; + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0; + else + has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0; + + e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0; + } + } + + e->merged_mask = 0; + for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) + e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1]; + } + } + } + + } + + if (check_required(e, me) < 0) + return -1; + + if (e->switch_use == PA_ALSA_SWITCH_SELECT) { + pa_alsa_option *o; + + PA_LLIST_FOREACH(o, e->options) + o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0; + } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) { + int n; + pa_alsa_option *o; + + if ((n = snd_mixer_selem_get_enum_items(me)) < 0) { + pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n)); + return -1; + } + + PA_LLIST_FOREACH(o, e->options) { + int i; + + for (i = 0; i < n; i++) { + char buf[128]; + + if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0) + continue; + + if (!pa_streq(buf, o->alsa_name)) + continue; + + o->alsa_idx = i; + } + } + } + + return 0; +} + +static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) { + pa_alsa_element *e; + + pa_assert(p); + pa_assert(section); + + if (prefixed) { + if (!pa_startswith(section, "Element ")) + return NULL; + + section += 8; + } + + /* This is not an element section, but an enum section? */ + if (strchr(section, ':')) + return NULL; + + if (p->last_element && pa_streq(p->last_element->alsa_name, section)) + return p->last_element; + + PA_LLIST_FOREACH(e, p->elements) + if (pa_streq(e->alsa_name, section)) + goto finish; + + e = pa_xnew0(pa_alsa_element, 1); + e->path = p; + e->alsa_name = pa_xstrdup(section); + e->direction = p->direction; + + PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e); + +finish: + p->last_element = e; + return e; +} + +static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) { + char *en; + const char *on; + pa_alsa_option *o; + pa_alsa_element *e; + + if (!pa_startswith(section, "Option ")) + return NULL; + + section += 7; + + /* This is not an enum section, but an element section? */ + if (!(on = strchr(section, ':'))) + return NULL; + + en = pa_xstrndup(section, on - section); + on++; + + if (p->last_option && + pa_streq(p->last_option->element->alsa_name, en) && + pa_streq(p->last_option->alsa_name, on)) { + pa_xfree(en); + return p->last_option; + } + + pa_assert_se(e = element_get(p, en, FALSE)); + pa_xfree(en); + + PA_LLIST_FOREACH(o, e->options) + if (pa_streq(o->alsa_name, on)) + goto finish; + + o = pa_xnew0(pa_alsa_option, 1); + o->element = e; + o->alsa_name = pa_xstrdup(on); + o->alsa_idx = -1; + + if (p->last_option && p->last_option->element == e) + PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o); + else + PA_LLIST_PREPEND(pa_alsa_option, e->options, o); + +finish: + p->last_option = o; + return o; +} + +static int element_parse_switch( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_element *e; + + pa_assert(p); + + if (!(e = element_get(p, section, TRUE))) { + pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section); + return -1; + } + + if (pa_streq(rvalue, "ignore")) + e->switch_use = PA_ALSA_SWITCH_IGNORE; + else if (pa_streq(rvalue, "mute")) + e->switch_use = PA_ALSA_SWITCH_MUTE; + else if (pa_streq(rvalue, "off")) + e->switch_use = PA_ALSA_SWITCH_OFF; + else if (pa_streq(rvalue, "on")) + e->switch_use = PA_ALSA_SWITCH_ON; + else if (pa_streq(rvalue, "select")) + e->switch_use = PA_ALSA_SWITCH_SELECT; + else { + pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section); + return -1; + } + + return 0; +} + +static int element_parse_volume( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_element *e; + + pa_assert(p); + + if (!(e = element_get(p, section, TRUE))) { + pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section); + return -1; + } + + if (pa_streq(rvalue, "ignore")) + e->volume_use = PA_ALSA_VOLUME_IGNORE; + else if (pa_streq(rvalue, "merge")) + e->volume_use = PA_ALSA_VOLUME_MERGE; + else if (pa_streq(rvalue, "off")) + e->volume_use = PA_ALSA_VOLUME_OFF; + else if (pa_streq(rvalue, "zero")) + e->volume_use = PA_ALSA_VOLUME_ZERO; + else { + pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section); + return -1; + } + + return 0; +} + +static int element_parse_enumeration( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_element *e; + + pa_assert(p); + + if (!(e = element_get(p, section, TRUE))) { + pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section); + return -1; + } + + if (pa_streq(rvalue, "ignore")) + e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE; + else if (pa_streq(rvalue, "select")) + e->enumeration_use = PA_ALSA_ENUMERATION_SELECT; + else { + pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section); + return -1; + } + + return 0; +} + +static int option_parse_priority( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_option *o; + uint32_t prio; + + pa_assert(p); + + if (!(o = option_get(p, section))) { + pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section); + return -1; + } + + if (pa_atou(rvalue, &prio) < 0) { + pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section); + return -1; + } + + o->priority = prio; + return 0; +} + +static int option_parse_name( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_option *o; + + pa_assert(p); + + if (!(o = option_get(p, section))) { + pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section); + return -1; + } + + pa_xfree(o->name); + o->name = pa_xstrdup(rvalue); + + return 0; +} + +static int element_parse_required( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_element *e; + pa_alsa_required_t req; + + pa_assert(p); + + if (!(e = element_get(p, section, TRUE))) { + pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section); + return -1; + } + + if (pa_streq(rvalue, "ignore")) + req = PA_ALSA_REQUIRED_IGNORE; + else if (pa_streq(rvalue, "switch")) + req = PA_ALSA_REQUIRED_SWITCH; + else if (pa_streq(rvalue, "volume")) + req = PA_ALSA_REQUIRED_VOLUME; + else if (pa_streq(rvalue, "enumeration")) + req = PA_ALSA_REQUIRED_ENUMERATION; + else if (pa_streq(rvalue, "any")) + req = PA_ALSA_REQUIRED_ANY; + else { + pa_log("[%s:%u] Required invalid of '%s'", filename, line, section); + return -1; + } + + if (pa_streq(lvalue, "required-absent")) + e->required_absent = req; + else + e->required = req; + + return 0; +} + +static int element_parse_direction( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_element *e; + + pa_assert(p); + + if (!(e = element_get(p, section, TRUE))) { + pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section); + return -1; + } + + if (pa_streq(rvalue, "playback")) + e->direction = PA_ALSA_DIRECTION_OUTPUT; + else if (pa_streq(rvalue, "capture")) + e->direction = PA_ALSA_DIRECTION_INPUT; + else { + pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section); + return -1; + } + + return 0; +} + +static int element_parse_direction_try_other( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_element *e; + int yes; + + if (!(e = element_get(p, section, TRUE))) { + pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section); + return -1; + } + + if ((yes = pa_parse_boolean(rvalue)) < 0) { + pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section); + return -1; + } + + e->direction_try_other = !!yes; + return 0; +} + +static pa_channel_position_mask_t parse_mask(const char *m) { + pa_channel_position_mask_t v; + + if (pa_streq(m, "all-left")) + v = PA_CHANNEL_POSITION_MASK_LEFT; + else if (pa_streq(m, "all-right")) + v = PA_CHANNEL_POSITION_MASK_RIGHT; + else if (pa_streq(m, "all-center")) + v = PA_CHANNEL_POSITION_MASK_CENTER; + else if (pa_streq(m, "all-front")) + v = PA_CHANNEL_POSITION_MASK_FRONT; + else if (pa_streq(m, "all-rear")) + v = PA_CHANNEL_POSITION_MASK_REAR; + else if (pa_streq(m, "all-side")) + v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER; + else if (pa_streq(m, "all-top")) + v = PA_CHANNEL_POSITION_MASK_TOP; + else if (pa_streq(m, "all-no-lfe")) + v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE); + else if (pa_streq(m, "all")) + v = PA_CHANNEL_POSITION_MASK_ALL; + else { + pa_channel_position_t p; + + if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID) + return 0; + + v = PA_CHANNEL_POSITION_MASK(p); + } + + return v; +} + +static int element_parse_override_map( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_element *e; + const char *state = NULL; + unsigned i = 0; + char *n; + + if (!(e = element_get(p, section, TRUE))) { + pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section); + return -1; + } + + while ((n = pa_split(rvalue, ",", &state))) { + pa_channel_position_mask_t m; + + if (!*n) + m = 0; + else { + if ((m = parse_mask(n)) == 0) { + pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section); + pa_xfree(n); + return -1; + } + } + + if (pa_streq(lvalue, "override-map.1")) + e->masks[i++][0] = m; + else + e->masks[i++][1] = m; + + /* Later on we might add override-map.3 and so on here ... */ + + pa_xfree(n); + } + + e->override_map = TRUE; + + return 0; +} + +static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) { + snd_mixer_selem_id_t *sid; + snd_mixer_elem_t *me; + int r; + + pa_assert(e); + pa_assert(m); + + SELEM_INIT(sid, e->alsa_name); + if (!(me = snd_mixer_find_selem(m, sid))) { + pa_log_warn("Element %s seems to have disappeared.", e->alsa_name); + return -1; + } + + if (e->switch_use == PA_ALSA_SWITCH_SELECT) { + + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx); + else + r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx); + + if (r < 0) + pa_log_warn("Faile to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno)); + + } else { + pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT); + + if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0) + pa_log_warn("Faile to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno)); + } + + return r; +} + +int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) { + pa_alsa_option *o; + uint32_t idx; + + pa_assert(s); + pa_assert(m); + + PA_IDXSET_FOREACH(o, s->options, idx) + element_set_option(o->element, m, o->alsa_idx); + + return 0; +} + +static int option_verify(pa_alsa_option *o) { + static const struct description_map well_known_descriptions[] = { + { "input", N_("Input") }, + { "input-docking", N_("Docking Station Input") }, + { "input-docking-microphone", N_("Docking Station Microphone") }, + { "input-linein", N_("Line-In") }, + { "input-microphone", N_("Microphone") }, + { "input-microphone-external", N_("External Microphone") }, + { "input-microphone-internal", N_("Internal Microphone") }, + { "input-radio", N_("Radio") }, + { "input-video", N_("Video") }, + { "input-agc-on", N_("Automatic Gain Control") }, + { "input-agc-off", "" }, + { "input-boost-on", N_("Boost") }, + { "input-boost-off", "" }, + { "output-amplifier-on", N_("Amplifier") }, + { "output-amplifier-off", "" } + }; + + pa_assert(o); + + if (!o->name) { + pa_log("No name set for option %s", o->alsa_name); + return -1; + } + + if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT && + o->element->switch_use != PA_ALSA_SWITCH_SELECT) { + pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name); + return -1; + } + + if (o->element->switch_use == PA_ALSA_SWITCH_SELECT && + !pa_streq(o->alsa_name, "on") && + !pa_streq(o->alsa_name, "off")) { + pa_log("Switch %s options need be named off or on ", o->element->alsa_name); + return -1; + } + + if (!o->description) + o->description = pa_xstrdup(lookup_description(o->name, + well_known_descriptions, + PA_ELEMENTSOF(well_known_descriptions))); + if (!o->description) + o->description = pa_xstrdup(o->name); + + return 0; +} + +static int element_verify(pa_alsa_element *e) { + pa_alsa_option *o; + + pa_assert(e); + + if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) || + (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) { + pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name); + return -1; + } + + if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) { + pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name); + return -1; + } + + PA_LLIST_FOREACH(o, e->options) + if (option_verify(o) < 0) + return -1; + + return 0; +} + +static int path_verify(pa_alsa_path *p) { + static const struct description_map well_known_descriptions[] = { + { "analog-input", N_("Analog Input") }, + { "analog-input-microphone", N_("Analog Microphone") }, + { "analog-input-linein", N_("Analog Line-In") }, + { "analog-input-radio", N_("Analog Radio") }, + { "analog-input-video", N_("Analog Video") }, + { "analog-output", N_("Analog Output") }, + { "analog-output-headphones", N_("Analog Headphones") }, + { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") }, + { "analog-output-mono", N_("Analog Mono Output") } + }; + + pa_alsa_element *e; + + pa_assert(p); + + PA_LLIST_FOREACH(e, p->elements) + if (element_verify(e) < 0) + return -1; + + if (!p->description) + p->description = pa_xstrdup(lookup_description(p->name, + well_known_descriptions, + PA_ELEMENTSOF(well_known_descriptions))); + + if (!p->description) + p->description = pa_xstrdup(p->name); + + return 0; +} + +pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) { + pa_alsa_path *p; + char *fn; + int r; + const char *n; + + pa_config_item items[] = { + /* [General] */ + { "priority", pa_config_parse_unsigned, NULL, "General" }, + { "description", pa_config_parse_string, NULL, "General" }, + { "name", pa_config_parse_string, NULL, "General" }, + + /* [Option ...] */ + { "priority", option_parse_priority, NULL, NULL }, + { "name", option_parse_name, NULL, NULL }, + + /* [Element ...] */ + { "switch", element_parse_switch, NULL, NULL }, + { "volume", element_parse_volume, NULL, NULL }, + { "enumeration", element_parse_enumeration, NULL, NULL }, + { "override-map.1", element_parse_override_map, NULL, NULL }, + { "override-map.2", element_parse_override_map, NULL, NULL }, + /* ... later on we might add override-map.3 and so on here ... */ + { "required", element_parse_required, NULL, NULL }, + { "required-absent", element_parse_required, NULL, NULL }, + { "direction", element_parse_direction, NULL, NULL }, + { "direction-try-other", element_parse_direction_try_other, NULL, NULL }, + { NULL, NULL, NULL, NULL } + }; + + pa_assert(fname); + + p = pa_xnew0(pa_alsa_path, 1); + n = pa_path_get_filename(fname); + p->name = pa_xstrndup(n, strcspn(n, ".")); + p->direction = direction; + + items[0].data = &p->priority; + items[1].data = &p->description; + items[2].data = &p->name; + + fn = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR); + r = pa_config_parse(fn, NULL, items, p); + pa_xfree(fn); + + if (r < 0) + goto fail; + + if (path_verify(p) < 0) + goto fail; + + return p; + +fail: + pa_alsa_path_free(p); + return NULL; +} + +pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) { + pa_alsa_path *p; + pa_alsa_element *e; + + pa_assert(element); + + p = pa_xnew0(pa_alsa_path, 1); + p->name = pa_xstrdup(element); + p->direction = direction; + + e = pa_xnew0(pa_alsa_element, 1); + e->path = p; + e->alsa_name = pa_xstrdup(element); + e->direction = direction; + + e->switch_use = PA_ALSA_SWITCH_MUTE; + e->volume_use = PA_ALSA_VOLUME_MERGE; + + PA_LLIST_PREPEND(pa_alsa_element, p->elements, e); + return p; +} + +static pa_bool_t element_drop_unsupported(pa_alsa_element *e) { + pa_alsa_option *o, *n; + + pa_assert(e); + + for (o = e->options; o; o = n) { + n = o->next; + + if (o->alsa_idx < 0) { + PA_LLIST_REMOVE(pa_alsa_option, e->options, o); + option_free(o); + } + } + + return + e->switch_use != PA_ALSA_SWITCH_IGNORE || + e->volume_use != PA_ALSA_VOLUME_IGNORE || + e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE; +} + +static void path_drop_unsupported(pa_alsa_path *p) { + pa_alsa_element *e, *n; + + pa_assert(p); + + for (e = p->elements; e; e = n) { + n = e->next; + + if (!element_drop_unsupported(e)) { + PA_LLIST_REMOVE(pa_alsa_element, p->elements, e); + element_free(e); + } + } +} + +static void path_make_options_unique(pa_alsa_path *p) { + pa_alsa_element *e; + pa_alsa_option *o, *u; + + PA_LLIST_FOREACH(e, p->elements) { + PA_LLIST_FOREACH(o, e->options) { + unsigned i; + char *m; + + for (u = o->next; u; u = u->next) + if (pa_streq(u->name, o->name)) + break; + + if (!u) + continue; + + m = pa_xstrdup(o->name); + + /* OK, this name is not unique, hence let's rename */ + for (i = 1, u = o; u; u = u->next) { + char *nn, *nd; + + if (!pa_streq(u->name, m)) + continue; + + nn = pa_sprintf_malloc("%s-%u", m, i); + pa_xfree(u->name); + u->name = nn; + + nd = pa_sprintf_malloc("%s %u", u->description, i); + pa_xfree(u->description); + u->description = nd; + + i++; + } + + pa_xfree(m); + } + } +} + +static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) { + pa_alsa_option *o; + + for (; e; e = e->next) + if (e->switch_use == PA_ALSA_SWITCH_SELECT || + e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) + break; + + if (!e) + return FALSE; + + for (o = e->options; o; o = o->next) { + pa_alsa_setting *s; + + if (template) { + s = pa_xnewdup(pa_alsa_setting, template, 1); + s->options = pa_idxset_copy(template->options); + s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name); + s->description = + (template->description[0] && o->description[0]) + ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description) + : (template->description[0] + ? pa_xstrdup(template->description) + : pa_xstrdup(o->description)); + + s->priority = PA_MAX(template->priority, o->priority); + } else { + s = pa_xnew0(pa_alsa_setting, 1); + s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + s->name = pa_xstrdup(o->name); + s->description = pa_xstrdup(o->description); + s->priority = o->priority; + } + + pa_idxset_put(s->options, o, NULL); + + if (element_create_settings(e->next, s)) + /* This is not a leaf, so let's get rid of it */ + setting_free(s); + else { + /* This is a leaf, so let's add it */ + PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s); + + e->path->last_setting = s; + } + } + + return TRUE; +} + +static void path_create_settings(pa_alsa_path *p) { + pa_assert(p); + + element_create_settings(p->elements, NULL); +} + +int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { + pa_alsa_element *e; + double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX]; + pa_channel_position_t t; + + pa_assert(p); + pa_assert(m); + + if (p->probed) + return 0; + + pa_zero(min_dB); + pa_zero(max_dB); + + pa_log_debug("Probing path '%s'", p->name); + + PA_LLIST_FOREACH(e, p->elements) { + if (element_probe(e, m) < 0) { + p->supported = FALSE; + pa_log_debug("Probe of element '%s' failed.", e->alsa_name); + return -1; + } + + if (ignore_dB) + e->has_dB = FALSE; + + if (e->volume_use == PA_ALSA_VOLUME_MERGE) { + + if (!p->has_volume) { + p->min_volume = e->min_volume; + p->max_volume = e->max_volume; + } + + if (e->has_dB) { + if (!p->has_volume) { + for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) + if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) { + min_dB[t] = e->min_dB; + max_dB[t] = e->max_dB; + } + + p->has_dB = TRUE; + } else { + + if (p->has_dB) { + for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) + if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) { + min_dB[t] += e->min_dB; + max_dB[t] += e->max_dB; + } + } else + /* Hmm, there's another element before us + * which cannot do dB volumes, so we we need + * to 'neutralize' this slider */ + e->volume_use = PA_ALSA_VOLUME_ZERO; + } + } else if (p->has_volume) + /* We can't use this volume, so let's ignore it */ + e->volume_use = PA_ALSA_VOLUME_IGNORE; + + p->has_volume = TRUE; + } + + if (e->switch_use == PA_ALSA_SWITCH_MUTE) + p->has_mute = TRUE; + } + + path_drop_unsupported(p); + path_make_options_unique(p); + path_create_settings(p); + + p->supported = TRUE; + p->probed = TRUE; + + p->min_dB = INFINITY; + p->max_dB = -INFINITY; + + for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) { + if (p->min_dB > min_dB[t]) + p->min_dB = min_dB[t]; + + if (p->max_dB < max_dB[t]) + p->max_dB = max_dB[t]; + } + + return 0; +} + +void pa_alsa_setting_dump(pa_alsa_setting *s) { + pa_assert(s); + + pa_log_debug("Setting %s (%s) priority=%u", + s->name, + pa_strnull(s->description), + s->priority); +} + +void pa_alsa_option_dump(pa_alsa_option *o) { + pa_assert(o); + + pa_log_debug("Option %s (%s/%s) index=%i, priority=%u", + o->alsa_name, + pa_strnull(o->name), + pa_strnull(o->description), + o->alsa_idx, + o->priority); +} + +void pa_alsa_element_dump(pa_alsa_element *e) { + pa_alsa_option *o; + pa_assert(e); + + pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s", + e->alsa_name, + e->direction, + e->switch_use, + e->volume_use, + e->enumeration_use, + e->required, + e->required_absent, + (long long unsigned) e->merged_mask, + e->n_channels, + pa_yes_no(e->override_map)); + + PA_LLIST_FOREACH(o, e->options) + pa_alsa_option_dump(o); +} + +void pa_alsa_path_dump(pa_alsa_path *p) { + pa_alsa_element *e; + pa_alsa_setting *s; + pa_assert(p); + + pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, " + "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g", + p->name, + pa_strnull(p->description), + p->direction, + p->priority, + pa_yes_no(p->probed), + pa_yes_no(p->supported), + pa_yes_no(p->has_mute), + pa_yes_no(p->has_volume), + pa_yes_no(p->has_dB), + p->min_volume, p->max_volume, + p->min_dB, p->max_dB); + + PA_LLIST_FOREACH(e, p->elements) + pa_alsa_element_dump(e); + + PA_LLIST_FOREACH(s, p->settings) + pa_alsa_setting_dump(s); +} + +static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) { + snd_mixer_selem_id_t *sid; + snd_mixer_elem_t *me; + + pa_assert(e); + pa_assert(m); + pa_assert(cb); + + SELEM_INIT(sid, e->alsa_name); + if (!(me = snd_mixer_find_selem(m, sid))) { + pa_log_warn("Element %s seems to have disappeared.", e->alsa_name); + return; + } + + snd_mixer_elem_set_callback(me, cb); + snd_mixer_elem_set_callback_private(me, userdata); +} + +void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) { + pa_alsa_element *e; + + pa_assert(p); + pa_assert(m); + pa_assert(cb); + + PA_LLIST_FOREACH(e, p->elements) + element_set_callback(e, m, cb, userdata); +} + +void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) { + pa_alsa_path *p; + + pa_assert(ps); + pa_assert(m); + pa_assert(cb); + + PA_LLIST_FOREACH(p, ps->paths) + pa_alsa_path_set_callback(p, m, cb, userdata); +} + +pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) { + pa_alsa_path_set *ps; + char **pn = NULL, **en = NULL, **ie; + + pa_assert(m); + pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT); + + if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction) + return NULL; + + ps = pa_xnew0(pa_alsa_path_set, 1); + ps->direction = direction; + + if (direction == PA_ALSA_DIRECTION_OUTPUT) + pn = m->output_path_names; + else if (direction == PA_ALSA_DIRECTION_INPUT) + pn = m->input_path_names; + + if (pn) { + char **in; + + for (in = pn; *in; in++) { + pa_alsa_path *p; + pa_bool_t duplicate = FALSE; + char **kn, *fn; + + for (kn = pn; kn != in; kn++) + if (pa_streq(*kn, *in)) { + duplicate = TRUE; + break; + } + + if (duplicate) + continue; + + fn = pa_sprintf_malloc("%s.conf", *in); + + if ((p = pa_alsa_path_new(fn, direction))) { + p->path_set = ps; + PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p); + ps->last_path = p; + } + + pa_xfree(fn); + } + + return ps; + } + + if (direction == PA_ALSA_DIRECTION_OUTPUT) + en = m->output_element; + else if (direction == PA_ALSA_DIRECTION_INPUT) + en = m->input_element; + + if (!en) { + pa_alsa_path_set_free(ps); + return NULL; + } + + for (ie = en; *ie; ie++) { + char **je; + pa_alsa_path *p; + + p = pa_alsa_path_synthesize(*ie, direction); + p->path_set = ps; + + /* Mark all other passed elements for require-absent */ + for (je = en; *je; je++) { + pa_alsa_element *e; + e = pa_xnew0(pa_alsa_element, 1); + e->path = p; + e->alsa_name = pa_xstrdup(*je); + e->direction = direction; + e->required_absent = PA_ALSA_REQUIRED_ANY; + + PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e); + p->last_element = e; + } + + PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p); + ps->last_path = p; + } + + return ps; +} + +void pa_alsa_path_set_dump(pa_alsa_path_set *ps) { + pa_alsa_path *p; + pa_assert(ps); + + pa_log_debug("Path Set %p, direction=%i, probed=%s", + (void*) ps, + ps->direction, + pa_yes_no(ps->probed)); + + PA_LLIST_FOREACH(p, ps->paths) + pa_alsa_path_dump(p); +} + +static void path_set_unify(pa_alsa_path_set *ps) { + pa_alsa_path *p; + pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE; + pa_assert(ps); + + /* We have issues dealing with paths that vary too wildly. That + * means for now we have to have all paths support volume/mute/dB + * or none. */ + + PA_LLIST_FOREACH(p, ps->paths) { + pa_assert(p->probed); + + if (!p->has_volume) + has_volume = FALSE; + else if (!p->has_dB) + has_dB = FALSE; + + if (!p->has_mute) + has_mute = FALSE; + } + + if (!has_volume || !has_dB || !has_mute) { + + if (!has_volume) + pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether."); + else if (!has_dB) + pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether."); + + if (!has_mute) + pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether."); + + PA_LLIST_FOREACH(p, ps->paths) { + if (!has_volume) + p->has_volume = FALSE; + else if (!has_dB) + p->has_dB = FALSE; + + if (!has_mute) + p->has_mute = FALSE; + } + } +} + +static void path_set_make_paths_unique(pa_alsa_path_set *ps) { + pa_alsa_path *p, *q; + + PA_LLIST_FOREACH(p, ps->paths) { + unsigned i; + char *m; + + for (q = p->next; q; q = q->next) + if (pa_streq(q->name, p->name)) + break; + + if (!q) + continue; + + m = pa_xstrdup(p->name); + + /* OK, this name is not unique, hence let's rename */ + for (i = 1, q = p; q; q = q->next) { + char *nn, *nd; + + if (!pa_streq(q->name, m)) + continue; + + nn = pa_sprintf_malloc("%s-%u", m, i); + pa_xfree(q->name); + q->name = nn; + + nd = pa_sprintf_malloc("%s %u", q->description, i); + pa_xfree(q->description); + q->description = nd; + + i++; + } + + pa_xfree(m); + } +} + +void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) { + pa_alsa_path *p, *n; + + pa_assert(ps); + + if (ps->probed) + return; + + for (p = ps->paths; p; p = n) { + n = p->next; + + if (pa_alsa_path_probe(p, m, ignore_dB) < 0) { + PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p); + pa_alsa_path_free(p); + } + } + + path_set_unify(ps); + path_set_make_paths_unique(ps); + ps->probed = TRUE; +} + +static void mapping_free(pa_alsa_mapping *m) { + pa_assert(m); + + pa_xfree(m->name); + pa_xfree(m->description); + + pa_xstrfreev(m->device_strings); + pa_xstrfreev(m->input_path_names); + pa_xstrfreev(m->output_path_names); + pa_xstrfreev(m->input_element); + pa_xstrfreev(m->output_element); + + pa_assert(!m->input_pcm); + pa_assert(!m->output_pcm); + + pa_xfree(m); +} + +static void profile_free(pa_alsa_profile *p) { + pa_assert(p); + + pa_xfree(p->name); + pa_xfree(p->description); + + pa_xstrfreev(p->input_mapping_names); + pa_xstrfreev(p->output_mapping_names); + + if (p->input_mappings) + pa_idxset_free(p->input_mappings, NULL, NULL); + + if (p->output_mappings) + pa_idxset_free(p->output_mappings, NULL, NULL); + + pa_xfree(p); +} + +void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) { + pa_assert(ps); + + if (ps->profiles) { + pa_alsa_profile *p; + + while ((p = pa_hashmap_steal_first(ps->profiles))) + profile_free(p); + + pa_hashmap_free(ps->profiles, NULL, NULL); + } + + if (ps->mappings) { + pa_alsa_mapping *m; + + while ((m = pa_hashmap_steal_first(ps->mappings))) + mapping_free(m); + + pa_hashmap_free(ps->mappings, NULL, NULL); + } + + pa_xfree(ps); +} + +static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) { + pa_alsa_mapping *m; + + if (!pa_startswith(name, "Mapping ")) + return NULL; + + name += 8; + + if ((m = pa_hashmap_get(ps->mappings, name))) + return m; + + m = pa_xnew0(pa_alsa_mapping, 1); + m->profile_set = ps; + m->name = pa_xstrdup(name); + pa_channel_map_init(&m->channel_map); + + pa_hashmap_put(ps->mappings, m->name, m); + + return m; +} + +static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) { + pa_alsa_profile *p; + + if (!pa_startswith(name, "Profile ")) + return NULL; + + name += 8; + + if ((p = pa_hashmap_get(ps->profiles, name))) + return p; + + p = pa_xnew0(pa_alsa_profile, 1); + p->profile_set = ps; + p->name = pa_xstrdup(name); + + pa_hashmap_put(ps->profiles, p->name, p); + + return p; +} + +static int mapping_parse_device_strings( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + pa_alsa_mapping *m; + + pa_assert(ps); + + if (!(m = mapping_get(ps, section))) { + pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section); + return -1; + } + + pa_xstrfreev(m->device_strings); + if (!(m->device_strings = pa_split_spaces_strv(rvalue))) { + pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section); + return -1; + } + + return 0; +} + +static int mapping_parse_channel_map( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + pa_alsa_mapping *m; + + pa_assert(ps); + + if (!(m = mapping_get(ps, section))) { + pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section); + return -1; + } + + if (!(pa_channel_map_parse(&m->channel_map, rvalue))) { + pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section); + return -1; + } + + return 0; +} + +static int mapping_parse_paths( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + pa_alsa_mapping *m; + + pa_assert(ps); + + if (!(m = mapping_get(ps, section))) { + pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section); + return -1; + } + + if (pa_streq(lvalue, "paths-input")) { + pa_xstrfreev(m->input_path_names); + m->input_path_names = pa_split_spaces_strv(rvalue); + } else { + pa_xstrfreev(m->output_path_names); + m->output_path_names = pa_split_spaces_strv(rvalue); + } + + return 0; +} + +static int mapping_parse_element( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + pa_alsa_mapping *m; + + pa_assert(ps); + + if (!(m = mapping_get(ps, section))) { + pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section); + return -1; + } + + if (pa_streq(lvalue, "element-input")) { + pa_xstrfreev(m->input_element); + m->input_element = pa_split_spaces_strv(rvalue); + } else { + pa_xstrfreev(m->output_element); + m->output_element = pa_split_spaces_strv(rvalue); + } + + return 0; +} + +static int mapping_parse_direction( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + pa_alsa_mapping *m; + + pa_assert(ps); + + if (!(m = mapping_get(ps, section))) { + pa_log("[%s:%u] Section name %s invalid.", filename, line, section); + return -1; + } + + if (pa_streq(rvalue, "input")) + m->direction = PA_ALSA_DIRECTION_INPUT; + else if (pa_streq(rvalue, "output")) + m->direction = PA_ALSA_DIRECTION_OUTPUT; + else if (pa_streq(rvalue, "any")) + m->direction = PA_ALSA_DIRECTION_ANY; + else { + pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue); + return -1; + } + + return 0; +} + +static int mapping_parse_description( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + pa_alsa_profile *p; + pa_alsa_mapping *m; + + pa_assert(ps); + + if ((m = mapping_get(ps, section))) { + pa_xstrdup(m->description); + m->description = pa_xstrdup(rvalue); + } else if ((p = profile_get(ps, section))) { + pa_xfree(p->description); + p->description = pa_xstrdup(rvalue); + } else { + pa_log("[%s:%u] Section name %s invalid.", filename, line, section); + return -1; + } + + return 0; +} + +static int mapping_parse_priority( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + pa_alsa_profile *p; + pa_alsa_mapping *m; + uint32_t prio; + + pa_assert(ps); + + if (pa_atou(rvalue, &prio) < 0) { + pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section); + return -1; + } + + if ((m = mapping_get(ps, section))) + m->priority = prio; + else if ((p = profile_get(ps, section))) + p->priority = prio; + else { + pa_log("[%s:%u] Section name %s invalid.", filename, line, section); + return -1; + } + + return 0; +} + +static int profile_parse_mappings( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + pa_alsa_profile *p; + + pa_assert(ps); + + if (!(p = profile_get(ps, section))) { + pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section); + return -1; + } + + if (pa_streq(lvalue, "input-mappings")) { + pa_xstrfreev(p->input_mapping_names); + p->input_mapping_names = pa_split_spaces_strv(rvalue); + } else { + pa_xstrfreev(p->output_mapping_names); + p->output_mapping_names = pa_split_spaces_strv(rvalue); + } + + return 0; +} + +static int profile_parse_skip_probe( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + pa_alsa_profile *p; + int b; + + pa_assert(ps); + + if (!(p = profile_get(ps, section))) { + pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section); + return -1; + } + + if ((b = pa_parse_boolean(rvalue)) < 0) { + pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section); + return -1; + } + + p->supported = b; + + return 0; +} + +static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) { + + static const struct description_map well_known_descriptions[] = { + { "analog-mono", N_("Analog Mono") }, + { "analog-stereo", N_("Analog Stereo") }, + { "analog-surround-21", N_("Analog Surround 2.1") }, + { "analog-surround-30", N_("Analog Surround 3.0") }, + { "analog-surround-31", N_("Analog Surround 3.1") }, + { "analog-surround-40", N_("Analog Surround 4.0") }, + { "analog-surround-41", N_("Analog Surround 4.1") }, + { "analog-surround-50", N_("Analog Surround 5.0") }, + { "analog-surround-51", N_("Analog Surround 5.1") }, + { "analog-surround-61", N_("Analog Surround 6.0") }, + { "analog-surround-61", N_("Analog Surround 6.1") }, + { "analog-surround-70", N_("Analog Surround 7.0") }, + { "analog-surround-71", N_("Analog Surround 7.1") }, + { "iec958-stereo", N_("Digital Stereo (IEC958)") }, + { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") }, + { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") }, + { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") }, + { "hdmi-stereo", N_("Digital Stereo (HDMI)") } + }; + + pa_assert(m); + + if (!pa_channel_map_valid(&m->channel_map)) { + pa_log("Mapping %s is missing channel map.", m->name); + return -1; + } + + if (!m->device_strings) { + pa_log("Mapping %s is missing device strings.", m->name); + return -1; + } + + if ((m->input_path_names && m->input_element) || + (m->output_path_names && m->output_element)) { + pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name); + return -1; + } + + if (!m->description) + m->description = pa_xstrdup(lookup_description(m->name, + well_known_descriptions, + PA_ELEMENTSOF(well_known_descriptions))); + + if (!m->description) + m->description = pa_xstrdup(m->name); + + if (bonus) { + if (pa_channel_map_equal(&m->channel_map, bonus)) + m->priority += 5000; + else if (m->channel_map.channels == bonus->channels) + m->priority += 4000; + } + + return 0; +} + +void pa_alsa_mapping_dump(pa_alsa_mapping *m) { + char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + pa_assert(m); + + pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i", + m->name, + pa_strnull(m->description), + m->priority, + pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map), + pa_yes_no(m->supported), + m->direction); +} + +static void profile_set_add_auto_pair( + pa_alsa_profile_set *ps, + pa_alsa_mapping *m, /* output */ + pa_alsa_mapping *n /* input */) { + + char *name; + pa_alsa_profile *p; + + pa_assert(ps); + pa_assert(m || n); + + if (m && m->direction == PA_ALSA_DIRECTION_INPUT) + return; + + if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT) + return; + + if (m && n) + name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name); + else if (m) + name = pa_sprintf_malloc("output:%s", m->name); + else + name = pa_sprintf_malloc("input:%s", n->name); + + if ((p = pa_hashmap_get(ps->profiles, name))) { + pa_xfree(name); + return; + } + + p = pa_xnew0(pa_alsa_profile, 1); + p->profile_set = ps; + p->name = name; + + if (m) { + p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + pa_idxset_put(p->output_mappings, m, NULL); + p->priority += m->priority * 100; + } + + if (n) { + p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + pa_idxset_put(p->input_mappings, n, NULL); + p->priority += n->priority; + } + + pa_hashmap_put(ps->profiles, p->name, p); +} + +static void profile_set_add_auto(pa_alsa_profile_set *ps) { + pa_alsa_mapping *m, *n; + void *m_state, *n_state; + + pa_assert(ps); + + PA_HASHMAP_FOREACH(m, ps->mappings, m_state) { + profile_set_add_auto_pair(ps, m, NULL); + + PA_HASHMAP_FOREACH(n, ps->mappings, n_state) + profile_set_add_auto_pair(ps, m, n); + } + + PA_HASHMAP_FOREACH(n, ps->mappings, n_state) + profile_set_add_auto_pair(ps, NULL, n); +} + +static int profile_verify(pa_alsa_profile *p) { + + static const struct description_map well_known_descriptions[] = { + { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") }, + { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") }, + { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") }, + { "off", N_("Off") } + }; + + pa_assert(p); + + /* Replace the output mapping names by the actual mappings */ + if (p->output_mapping_names) { + char **name; + + pa_assert(!p->output_mappings); + p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + + for (name = p->output_mapping_names; *name; name++) { + pa_alsa_mapping *m; + char **in; + pa_bool_t duplicate = FALSE; + + for (in = p->output_mapping_names; *in; in++) + if (pa_streq(*name, *in)) { + duplicate = TRUE; + break; + } + + if (duplicate) + continue; + + if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) { + pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name); + return -1; + } + + pa_idxset_put(p->output_mappings, m, NULL); + } + + pa_xstrfreev(p->output_mapping_names); + p->output_mapping_names = NULL; + } + + /* Replace the input mapping names by the actual mappings */ + if (p->input_mapping_names) { + char **name; + + pa_assert(!p->input_mappings); + p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + + for (name = p->input_mapping_names; *name; name++) { + pa_alsa_mapping *m; + char **in; + pa_bool_t duplicate = FALSE; + + for (in = p->input_mapping_names; *in; in++) + if (pa_streq(*name, *in)) { + duplicate = TRUE; + break; + } + + if (duplicate) + continue; + + if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) { + pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name); + return -1; + } + + pa_idxset_put(p->input_mappings, m, NULL); + } + + pa_xstrfreev(p->input_mapping_names); + p->input_mapping_names = NULL; + } + + if (!p->input_mappings && !p->output_mappings) { + pa_log("Profile '%s' lacks mappings.", p->name); + return -1; + } + + if (!p->description) + p->description = pa_xstrdup(lookup_description(p->name, + well_known_descriptions, + PA_ELEMENTSOF(well_known_descriptions))); + + if (!p->description) { + pa_strbuf *sb; + uint32_t idx; + pa_alsa_mapping *m; + + sb = pa_strbuf_new(); + + if (p->output_mappings) + PA_IDXSET_FOREACH(m, p->output_mappings, idx) { + if (!pa_strbuf_isempty(sb)) + pa_strbuf_puts(sb, " + "); + + pa_strbuf_printf(sb, "%s Output", m->description); + } + + if (p->input_mappings) + PA_IDXSET_FOREACH(m, p->input_mappings, idx) { + if (!pa_strbuf_isempty(sb)) + pa_strbuf_puts(sb, " + "); + + pa_strbuf_printf(sb, "%s Input", m->description); + } + + p->description = pa_strbuf_tostring_free(sb); + } + + return 0; +} + +void pa_alsa_profile_dump(pa_alsa_profile *p) { + uint32_t idx; + pa_alsa_mapping *m; + pa_assert(p); + + pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u", + p->name, + pa_strnull(p->description), + p->priority, + pa_yes_no(p->supported), + p->input_mappings ? pa_idxset_size(p->input_mappings) : 0, + p->output_mappings ? pa_idxset_size(p->output_mappings) : 0); + + if (p->input_mappings) + PA_IDXSET_FOREACH(m, p->input_mappings, idx) + pa_log_debug("Input %s", m->name); + + if (p->output_mappings) + PA_IDXSET_FOREACH(m, p->output_mappings, idx) + pa_log_debug("Output %s", m->name); +} + +pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) { + pa_alsa_profile_set *ps; + pa_alsa_profile *p; + pa_alsa_mapping *m; + char *fn; + int r; + void *state; + + static pa_config_item items[] = { + /* [General] */ + { "auto-profiles", pa_config_parse_bool, NULL, "General" }, + + /* [Mapping ...] */ + { "device-strings", mapping_parse_device_strings, NULL, NULL }, + { "channel-map", mapping_parse_channel_map, NULL, NULL }, + { "paths-input", mapping_parse_paths, NULL, NULL }, + { "paths-output", mapping_parse_paths, NULL, NULL }, + { "element-input", mapping_parse_element, NULL, NULL }, + { "element-output", mapping_parse_element, NULL, NULL }, + { "direction", mapping_parse_direction, NULL, NULL }, + + /* Shared by [Mapping ...] and [Profile ...] */ + { "description", mapping_parse_description, NULL, NULL }, + { "priority", mapping_parse_priority, NULL, NULL }, + + /* [Profile ...] */ + { "input-mappings", profile_parse_mappings, NULL, NULL }, + { "output-mappings", profile_parse_mappings, NULL, NULL }, + { "skip-probe", profile_parse_skip_probe, NULL, NULL }, + { NULL, NULL, NULL, NULL } + }; + + ps = pa_xnew0(pa_alsa_profile_set, 1); + ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + items[0].data = &ps->auto_profiles; + + if (!fname) + fname = "default.conf"; + + fn = pa_maybe_prefix_path(fname, PA_ALSA_PROFILE_SETS_DIR); + r = pa_config_parse(fn, NULL, items, ps); + pa_xfree(fn); + + if (r < 0) + goto fail; + + PA_HASHMAP_FOREACH(m, ps->mappings, state) + if (mapping_verify(m, bonus) < 0) + goto fail; + + if (ps->auto_profiles) + profile_set_add_auto(ps); + + PA_HASHMAP_FOREACH(p, ps->profiles, state) + if (profile_verify(p) < 0) + goto fail; + + return ps; + +fail: + pa_alsa_profile_set_free(ps); + return NULL; +} + +void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss) { + void *state; + pa_alsa_profile *p, *last = NULL; + pa_alsa_mapping *m; + + pa_assert(ps); + pa_assert(dev_id); + pa_assert(ss); + + if (ps->probed) + return; + + PA_HASHMAP_FOREACH(p, ps->profiles, state) { + pa_sample_spec try_ss; + pa_channel_map try_map; + uint32_t idx; + + /* Is this already marked that it is supported? (i.e. from the config file) */ + if (p->supported) + continue; + + pa_log_debug("Looking at profile %s", p->name); + + /* Close PCMs from the last iteration we don't need anymore */ + if (last && last->output_mappings) + PA_IDXSET_FOREACH(m, last->output_mappings, idx) { + + if (!m->output_pcm) + break; + + if (last->supported) + m->supported++; + + if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) { + snd_pcm_close(m->output_pcm); + m->output_pcm = NULL; + } + } + + if (last && last->input_mappings) + PA_IDXSET_FOREACH(m, last->input_mappings, idx) { + + if (!m->input_pcm) + break; + + if (last->supported) + m->supported++; + + if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) { + snd_pcm_close(m->input_pcm); + m->input_pcm = NULL; + } + } + + p->supported = TRUE; + + /* Check if we can open all new ones */ + if (p->output_mappings) + PA_IDXSET_FOREACH(m, p->output_mappings, idx) { + + if (m->output_pcm) + continue; + + pa_log_debug("Checking for playback on %s (%s)", m->description, m->name); + try_map = m->channel_map; + try_ss = *ss; + try_ss.channels = try_map.channels; + + if (!(m ->output_pcm = pa_alsa_open_by_device_string_strv( + m->device_strings, + dev_id, + NULL, + &try_ss, &try_map, + SND_PCM_STREAM_PLAYBACK, + NULL, NULL, 0, NULL, NULL, + TRUE))) { + p->supported = FALSE; + break; + } + } + + if (p->input_mappings && p->supported) + PA_IDXSET_FOREACH(m, p->input_mappings, idx) { + + if (m->input_pcm) + continue; + + pa_log_debug("Checking for recording on %s (%s)", m->description, m->name); + try_map = m->channel_map; + try_ss = *ss; + try_ss.channels = try_map.channels; + + if (!(m ->input_pcm = pa_alsa_open_by_device_string_strv( + m->device_strings, + dev_id, + NULL, + &try_ss, &try_map, + SND_PCM_STREAM_CAPTURE, + NULL, NULL, 0, NULL, NULL, + TRUE))) { + p->supported = FALSE; + break; + } + } + + last = p; + + if (p->supported) + pa_log_debug("Profile %s supported.", p->name); + } + + /* Clean up */ + if (last) { + uint32_t idx; + + if (last->output_mappings) + PA_IDXSET_FOREACH(m, last->output_mappings, idx) + if (m->output_pcm) { + + if (last->supported) + m->supported++; + + snd_pcm_close(m->output_pcm); + m->output_pcm = NULL; + } + + if (last->input_mappings) + PA_IDXSET_FOREACH(m, last->input_mappings, idx) + if (m->input_pcm) { + + if (last->supported) + m->supported++; + + snd_pcm_close(m->input_pcm); + m->input_pcm = NULL; + } + } + + PA_HASHMAP_FOREACH(p, ps->profiles, state) + if (!p->supported) { + pa_hashmap_remove(ps->profiles, p->name); + profile_free(p); + } + + PA_HASHMAP_FOREACH(m, ps->mappings, state) + if (m->supported <= 0) { + pa_hashmap_remove(ps->mappings, m->name); + mapping_free(m); + } + + ps->probed = TRUE; +} + +void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) { + pa_alsa_profile *p; + pa_alsa_mapping *m; + void *state; + + pa_assert(ps); + + pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u", + (void*) + ps, + pa_yes_no(ps->auto_profiles), + pa_yes_no(ps->probed), + pa_hashmap_size(ps->mappings), + pa_hashmap_size(ps->profiles)); + + PA_HASHMAP_FOREACH(m, ps->mappings, state) + pa_alsa_mapping_dump(m); + + PA_HASHMAP_FOREACH(p, ps->profiles, state) + pa_alsa_profile_dump(p); +} + +void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) { + pa_alsa_path *path; + + pa_assert(p); + pa_assert(!*p); + pa_assert(ps); + + /* if there is no path, we don't want a port list */ + if (!ps->paths) + return; + + if (!ps->paths->next){ + pa_alsa_setting *s; + + /* If there is only one path, but no or only one setting, then + * we want a port list either */ + if (!ps->paths->settings || !ps->paths->settings->next) + return; + + /* Ok, there is only one path, however with multiple settings, + * so let's create a port for each setting */ + *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + PA_LLIST_FOREACH(s, ps->paths->settings) { + pa_device_port *port; + pa_alsa_port_data *data; + + port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data)); + port->priority = s->priority; + + data = PA_DEVICE_PORT_DATA(port); + data->path = ps->paths; + data->setting = s; + + pa_hashmap_put(*p, port->name, port); + } + + } else { + + /* We have multiple paths, so let's create a port for each + * one, and each of each settings */ + *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + PA_LLIST_FOREACH(path, ps->paths) { + + if (!path->settings || !path->settings->next) { + pa_device_port *port; + pa_alsa_port_data *data; + + /* If there is no or just one setting we only need a + * single entry */ + + port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data)); + port->priority = path->priority * 100; + + + data = PA_DEVICE_PORT_DATA(port); + data->path = path; + data->setting = path->settings; + + pa_hashmap_put(*p, port->name, port); + } else { + pa_alsa_setting *s; + + PA_LLIST_FOREACH(s, path->settings) { + pa_device_port *port; + pa_alsa_port_data *data; + char *n, *d; + + n = pa_sprintf_malloc("%s;%s", path->name, s->name); + + if (s->description[0]) + d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description); + else + d = pa_xstrdup(path->description); + + port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data)); + port->priority = path->priority * 100 + s->priority; + + pa_xfree(n); + pa_xfree(d); + + data = PA_DEVICE_PORT_DATA(port); + data->path = path; + data->setting = s; + + pa_hashmap_put(*p, port->name, port); + } + } + } + } + + pa_log_debug("Added %u ports", pa_hashmap_size(*p)); +} diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h new file mode 100644 index 00000000..76788183 --- /dev/null +++ b/src/modules/alsa/alsa-mixer.h @@ -0,0 +1,292 @@ +#ifndef fooalsamixerhfoo +#define fooalsamixerhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef struct pa_alsa_fdlist pa_alsa_fdlist; +typedef struct pa_alsa_setting pa_alsa_setting; +typedef struct pa_alsa_option pa_alsa_option; +typedef struct pa_alsa_element pa_alsa_element; +typedef struct pa_alsa_path pa_alsa_path; +typedef struct pa_alsa_path_set pa_alsa_path_set; +typedef struct pa_alsa_mapping pa_alsa_mapping; +typedef struct pa_alsa_profile pa_alsa_profile; +typedef struct pa_alsa_profile_set pa_alsa_profile_set; +typedef struct pa_alsa_port_data pa_alsa_port_data; + +#include "alsa-util.h" + +typedef enum pa_alsa_switch_use { + PA_ALSA_SWITCH_IGNORE, + PA_ALSA_SWITCH_MUTE, /* make this switch follow mute status */ + PA_ALSA_SWITCH_OFF, /* set this switch to 'off' unconditionally */ + PA_ALSA_SWITCH_ON, /* set this switch to 'on' unconditionally */ + PA_ALSA_SWITCH_SELECT /* allow the user to select switch status through a setting */ +} pa_alsa_switch_use_t; + +typedef enum pa_alsa_volume_use { + PA_ALSA_VOLUME_IGNORE, + PA_ALSA_VOLUME_MERGE, /* merge this volume slider into the global volume slider */ + PA_ALSA_VOLUME_OFF, /* set this volume to minimal unconditionally */ + PA_ALSA_VOLUME_ZERO /* set this volume to 0dB unconditionally */ +} pa_alsa_volume_use_t; + +typedef enum pa_alsa_enumeration_use { + PA_ALSA_ENUMERATION_IGNORE, + PA_ALSA_ENUMERATION_SELECT +} pa_alsa_enumeration_use_t; + +typedef enum pa_alsa_required { + PA_ALSA_REQUIRED_IGNORE, + PA_ALSA_REQUIRED_SWITCH, + PA_ALSA_REQUIRED_VOLUME, + PA_ALSA_REQUIRED_ENUMERATION, + PA_ALSA_REQUIRED_ANY +} pa_alsa_required_t; + +typedef enum pa_alsa_direction { + PA_ALSA_DIRECTION_ANY, + PA_ALSA_DIRECTION_OUTPUT, + PA_ALSA_DIRECTION_INPUT +} pa_alsa_direction_t; + +/* A setting combines a couple of options into a single entity that + * may be selected. Only one setting can be active at the same + * time. */ +struct pa_alsa_setting { + pa_alsa_path *path; + PA_LLIST_FIELDS(pa_alsa_setting); + + pa_idxset *options; + + char *name; + char *description; + unsigned priority; +}; + +/* An option belongs to an element and refers to one enumeration item + * of the element is an enumeration item, or a switch status if the + * element is a switch item. */ +struct pa_alsa_option { + pa_alsa_element *element; + PA_LLIST_FIELDS(pa_alsa_option); + + char *alsa_name; + int alsa_idx; + + char *name; + char *description; + unsigned priority; +}; + +/* And element wraps one specific ALSA element. A series of elements * +make up a path (see below). If the element is an enumeration or switch +* element it may includes a list of options. */ +struct pa_alsa_element { + pa_alsa_path *path; + PA_LLIST_FIELDS(pa_alsa_element); + + char *alsa_name; + pa_alsa_direction_t direction; + + pa_alsa_switch_use_t switch_use; + pa_alsa_volume_use_t volume_use; + pa_alsa_enumeration_use_t enumeration_use; + + pa_alsa_required_t required; + pa_alsa_required_t required_absent; + + pa_bool_t override_map:1; + pa_bool_t direction_try_other:1; + + pa_bool_t has_dB:1; + long min_volume, max_volume; + double min_dB, max_dB; + + pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2]; + unsigned n_channels; + + pa_channel_position_mask_t merged_mask; + + PA_LLIST_HEAD(pa_alsa_option, options); +}; + +/* A path wraps a series of elements into a single entity which can be + * used to control it as if it had a single volume slider, a single + * mute switch and a single list of selectable options. */ +struct pa_alsa_path { + pa_alsa_path_set *path_set; + PA_LLIST_FIELDS(pa_alsa_path); + + pa_alsa_direction_t direction; + + char *name; + char *description; + unsigned priority; + + pa_bool_t probed:1; + pa_bool_t supported:1; + pa_bool_t has_mute:1; + pa_bool_t has_volume:1; + pa_bool_t has_dB:1; + + long min_volume, max_volume; + double min_dB, max_dB; + + /* This is used during parsing only, as a shortcut so that we + * don't have to iterate the list all the time */ + pa_alsa_element *last_element; + pa_alsa_option *last_option; + pa_alsa_setting *last_setting; + + PA_LLIST_HEAD(pa_alsa_element, elements); + PA_LLIST_HEAD(pa_alsa_setting, settings); +}; + +/* A path set is simply a set of paths that are applicable to a + * device */ +struct pa_alsa_path_set { + PA_LLIST_HEAD(pa_alsa_path, paths); + pa_alsa_direction_t direction; + pa_bool_t probed:1; + + /* This is used during parsing only, as a shortcut so that we + * don't have to iterate the list all the time */ + pa_alsa_path *last_path; +}; + +int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m); +void pa_alsa_setting_dump(pa_alsa_setting *s); + +void pa_alsa_option_dump(pa_alsa_option *o); + +void pa_alsa_element_dump(pa_alsa_element *e); + +pa_alsa_path *pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction); +pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction); +int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB); +void pa_alsa_path_dump(pa_alsa_path *p); +int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v); +int pa_alsa_path_get_mute(pa_alsa_path *path, snd_mixer_t *m, pa_bool_t *muted); +int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v); +int pa_alsa_path_set_mute(pa_alsa_path *path, snd_mixer_t *m, pa_bool_t muted); +int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m); +void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata); +void pa_alsa_path_free(pa_alsa_path *p); + +pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction); +void pa_alsa_path_set_probe(pa_alsa_path_set *s, snd_mixer_t *m, pa_bool_t ignore_dB); +void pa_alsa_path_set_dump(pa_alsa_path_set *s); +void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata); +void pa_alsa_path_set_free(pa_alsa_path_set *s); + +struct pa_alsa_mapping { + pa_alsa_profile_set *profile_set; + + char *name; + char *description; + unsigned priority; + pa_alsa_direction_t direction; + + pa_channel_map channel_map; + + char **device_strings; + + char **input_path_names; + char **output_path_names; + char **input_element; /* list of fallbacks */ + char **output_element; + + unsigned supported; + + /* Temporarily used during probing */ + snd_pcm_t *input_pcm; + snd_pcm_t *output_pcm; + + pa_sink *sink; + pa_source *source; +}; + +struct pa_alsa_profile { + pa_alsa_profile_set *profile_set; + + char *name; + char *description; + unsigned priority; + + pa_bool_t supported:1; + + char **input_mapping_names; + char **output_mapping_names; + + pa_idxset *input_mappings; + pa_idxset *output_mappings; +}; + +struct pa_alsa_profile_set { + pa_hashmap *mappings; + pa_hashmap *profiles; + + pa_bool_t auto_profiles; + pa_bool_t probed:1; +}; + +void pa_alsa_mapping_dump(pa_alsa_mapping *m); +void pa_alsa_profile_dump(pa_alsa_profile *p); + +pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus); +void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss); +void pa_alsa_profile_set_free(pa_alsa_profile_set *s); +void pa_alsa_profile_set_dump(pa_alsa_profile_set *s); + +snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device); + +pa_alsa_fdlist *pa_alsa_fdlist_new(void); +void pa_alsa_fdlist_free(pa_alsa_fdlist *fdl); +int pa_alsa_fdlist_set_mixer(pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m); + +/* Data structure for inclusion in pa_device_port for alsa + * sinks/sources. This contains nothing that needs to be freed + * individually */ +struct pa_alsa_port_data { + pa_alsa_path *path; + pa_alsa_setting *setting; +}; + +void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps); + +#endif diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 59a5ca75..2226bc6f 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -80,11 +80,9 @@ struct userdata { pa_alsa_fdlist *mixer_fdl; snd_mixer_t *mixer_handle; - snd_mixer_elem_t *mixer_elem; - long hw_volume_max, hw_volume_min; - long hw_dB_max, hw_dB_min; - pa_bool_t hw_dB_supported:1; - pa_bool_t mixer_seperate_channels:1; + pa_alsa_path_set *mixer_path_set; + pa_alsa_path *mixer_path; + pa_cvolume hardware_volume; size_t @@ -100,7 +98,8 @@ struct userdata { unsigned nfragments; pa_memchunk memchunk; - char *device_name; + char *device_name; /* name of the PCM device */ + char *control_device; /* name of the control device */ pa_bool_t use_mmap:1, use_tsched:1; @@ -991,191 +990,58 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { return 0; } -static pa_volume_t from_alsa_volume(struct userdata *u, long alsa_vol) { - - return (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / - (double) (u->hw_volume_max - u->hw_volume_min)); -} - -static long to_alsa_volume(struct userdata *u, pa_volume_t vol) { - long alsa_vol; - - alsa_vol = (long) round(((double) vol * (double) (u->hw_volume_max - u->hw_volume_min)) - / PA_VOLUME_NORM) + u->hw_volume_min; - - return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); -} - static void sink_get_volume_cb(pa_sink *s) { struct userdata *u = s->userdata; - int err; - unsigned i; pa_cvolume r; char t[PA_CVOLUME_SNPRINT_MAX]; pa_assert(u); - pa_assert(u->mixer_elem); - - if (u->mixer_seperate_channels) { - - r.channels = s->sample_spec.channels; - - for (i = 0; i < s->sample_spec.channels; i++) { - long alsa_vol; - - if (u->hw_dB_supported) { - - if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) - goto fail; - -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); -#endif - - r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0); - } else { - - if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) - goto fail; - - r.values[i] = from_alsa_volume(u, alsa_vol); - } - } - - } else { - long alsa_vol; - - if (u->hw_dB_supported) { - - if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) - goto fail; - -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); -#endif - - pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); - - } else { + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); - if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) - goto fail; + if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) + return; - pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol)); - } - } + /* Shift down by the base volume, so that 0dB becomes maximum volume */ + pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume); pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); - if (!pa_cvolume_equal(&u->hardware_volume, &r)) { + if (pa_cvolume_equal(&u->hardware_volume, &r)) + return; - s->virtual_volume = u->hardware_volume = r; + s->virtual_volume = u->hardware_volume = r; - if (u->hw_dB_supported) { - pa_cvolume reset; + if (u->mixer_path->has_dB) { + pa_cvolume reset; - /* Hmm, so the hardware volume changed, let's reset our software volume */ - pa_cvolume_reset(&reset, s->sample_spec.channels); - pa_sink_set_soft_volume(s, &reset); - } + /* Hmm, so the hardware volume changed, let's reset our software volume */ + pa_cvolume_reset(&reset, s->sample_spec.channels); + pa_sink_set_soft_volume(s, &reset); } - - return; - -fail: - pa_log_error("Unable to read volume: %s", pa_alsa_strerror(err)); } static void sink_set_volume_cb(pa_sink *s) { struct userdata *u = s->userdata; - int err; - unsigned i; pa_cvolume r; + char t[PA_CVOLUME_SNPRINT_MAX]; pa_assert(u); - pa_assert(u->mixer_elem); - - if (u->mixer_seperate_channels) { - - r.channels = s->sample_spec.channels; - - for (i = 0; i < s->sample_spec.channels; i++) { - long alsa_vol; - pa_volume_t vol; - - vol = s->virtual_volume.values[i]; - - if (u->hw_dB_supported) { - - alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); - alsa_vol += u->hw_dB_max; - alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); - - if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0) - goto fail; - - if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) - goto fail; - -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); -#endif - - r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0); - - } else { - alsa_vol = to_alsa_volume(u, vol); - - if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) - goto fail; - - if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) - goto fail; - - r.values[i] = from_alsa_volume(u, alsa_vol); - } - } - - } else { - pa_volume_t vol; - long alsa_vol; - - vol = pa_cvolume_max(&s->virtual_volume); - - if (u->hw_dB_supported) { - alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); - alsa_vol += u->hw_dB_max; - alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); - - if ((err = snd_mixer_selem_set_playback_dB_all(u->mixer_elem, alsa_vol, 1)) < 0) - goto fail; - - if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) - goto fail; - -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); -#endif - - pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); - - } else { - alsa_vol = to_alsa_volume(u, vol); + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); - if ((err = snd_mixer_selem_set_playback_volume_all(u->mixer_elem, alsa_vol)) < 0) - goto fail; + /* Shift up by the base volume */ + pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume); - if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) - goto fail; + if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) + return; - pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol)); - } - } + /* Shift down by the base volume, so that 0dB becomes maximum volume */ + pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume); u->hardware_volume = r; - if (u->hw_dB_supported) { - char t[PA_CVOLUME_SNPRINT_MAX]; + if (u->mixer_path->has_dB) { /* Match exactly what the user requested by software */ pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); @@ -1184,45 +1050,75 @@ static void sink_set_volume_cb(pa_sink *s) { pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); - } else + } else { + pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); /* We can't match exactly what the user requested, hence let's * at least tell the user about it */ s->virtual_volume = r; - - return; - -fail: - pa_log_error("Unable to set volume: %s", pa_alsa_strerror(err)); + } } static void sink_get_mute_cb(pa_sink *s) { struct userdata *u = s->userdata; - int err, sw = 0; + pa_bool_t b; pa_assert(u); - pa_assert(u->mixer_elem); + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); - if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) { - pa_log_error("Unable to get switch: %s", pa_alsa_strerror(err)); + if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0) return; - } - s->muted = !sw; + s->muted = b; } static void sink_set_mute_cb(pa_sink *s) { struct userdata *u = s->userdata; - int err; pa_assert(u); - pa_assert(u->mixer_elem); + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); - if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) { - pa_log_error("Unable to set switch: %s", pa_alsa_strerror(err)); - return; + pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted); +} + +static int sink_set_port_cb(pa_sink *s, pa_device_port *p) { + struct userdata *u = s->userdata; + pa_alsa_port_data *data; + + pa_assert(u); + pa_assert(p); + pa_assert(u->mixer_handle); + + data = PA_DEVICE_PORT_DATA(p); + + pa_assert_se(u->mixer_path = data->path); + pa_alsa_path_select(u->mixer_path, u->mixer_handle); + + if (u->mixer_path->has_volume && u->mixer_path->has_dB) { + s->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB); + s->n_volume_steps = PA_VOLUME_NORM+1; + + if (u->mixer_path->max_dB > 0.0) + pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(s->base_volume)); + else + pa_log_info("No particular base volume set, fixing to 0 dB"); + } else { + s->base_volume = PA_VOLUME_NORM; + s->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1; } + + if (data->setting) + pa_alsa_setting_select(data->setting, u->mixer_handle); + + if (s->set_mute) + s->set_mute(s); + if (s->set_volume) + s->set_volume(s); + + return 0; } static void sink_update_requested_latency_cb(pa_sink *s) { @@ -1465,77 +1361,127 @@ static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *de pa_xfree(t); } +static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, pa_bool_t ignore_dB) { + + if (!mapping && !element) + return; + + if (!(u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device))) { + pa_log_info("Failed to find a working mixer device."); + return; + } + + if (element) { + + if (!(u->mixer_path = pa_alsa_path_synthesize(element, PA_ALSA_DIRECTION_OUTPUT))) + goto fail; + + if (pa_alsa_path_probe(u->mixer_path, u->mixer_handle, ignore_dB) < 0) + goto fail; + + pa_log_debug("Probed mixer path %s:", u->mixer_path->name); + pa_alsa_path_dump(u->mixer_path); + } else { + + if (!(u->mixer_path_set = pa_alsa_path_set_new(mapping, PA_ALSA_DIRECTION_OUTPUT))) + goto fail; + + pa_alsa_path_set_probe(u->mixer_path_set, u->mixer_handle, ignore_dB); + + pa_log_debug("Probed mixer paths:"); + pa_alsa_path_set_dump(u->mixer_path_set); + } + + return; + +fail: + + if (u->mixer_path_set) { + pa_alsa_path_set_free(u->mixer_path_set); + u->mixer_path_set = NULL; + } else if (u->mixer_path) { + pa_alsa_path_free(u->mixer_path); + u->mixer_path = NULL; + } + + if (u->mixer_handle) { + snd_mixer_close(u->mixer_handle); + u->mixer_handle = NULL; + } +} + static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) { pa_assert(u); if (!u->mixer_handle) return 0; - pa_assert(u->mixer_elem); + if (u->sink->active_port) { + pa_alsa_port_data *data; - if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) { - pa_bool_t suitable = FALSE; + /* We have a list of supported paths, so let's activate the + * one that has been chosen as active */ - if (snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0) - pa_log_info("Failed to get volume range. Falling back to software volume control."); - else if (u->hw_volume_min >= u->hw_volume_max) - pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", u->hw_volume_min, u->hw_volume_max); - else { - pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max); - suitable = TRUE; - } + data = PA_DEVICE_PORT_DATA(u->sink->active_port); + u->mixer_path = data->path; - if (suitable) { - if (ignore_dB || snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0) - pa_log_info("Mixer doesn't support dB information or data is ignored."); - else { -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min)); - VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max)); -#endif + pa_alsa_path_select(data->path, u->mixer_handle); - if (u->hw_dB_min >= u->hw_dB_max) - pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0); - else { - pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0); - u->hw_dB_supported = TRUE; - - if (u->hw_dB_max > 0) { - u->sink->base_volume = pa_sw_volume_from_dB(- (double) u->hw_dB_max/100.0); - pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->sink->base_volume)); - } else - pa_log_info("No particular base volume set, fixing to 0 dB"); - } - } + if (data->setting) + pa_alsa_setting_select(data->setting, u->mixer_handle); - if (!u->hw_dB_supported && - u->hw_volume_max - u->hw_volume_min < 3) { + } else { - pa_log_info("Device doesn't do dB volume and has less than 4 volume levels. Falling back to software volume control."); - suitable = FALSE; - } - } + if (!u->mixer_path && u->mixer_path_set) + u->mixer_path = u->mixer_path_set->paths; - if (suitable) { - u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &u->sink->channel_map, u->mixer_map, TRUE) >= 0; + if (u->mixer_path) { + /* Hmm, we have only a single path, then let's activate it */ - u->sink->get_volume = sink_get_volume_cb; - u->sink->set_volume = sink_set_volume_cb; - u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0); - pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported"); + pa_alsa_path_select(u->mixer_path, u->mixer_handle); - if (!u->hw_dB_supported) - u->sink->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1; + if (u->mixer_path->settings) + pa_alsa_setting_select(u->mixer_path->settings, u->mixer_handle); } else - pa_log_info("Using software volume control."); + return 0; } - if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) { + if (!u->mixer_path->has_volume) + pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); + else { + + if (u->mixer_path->has_dB) { + pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB); + + u->sink->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB); + u->sink->n_volume_steps = PA_VOLUME_NORM+1; + + if (u->mixer_path->max_dB > 0.0) + pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->sink->base_volume)); + else + pa_log_info("No particular base volume set, fixing to 0 dB"); + + } else { + pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume); + u->sink->base_volume = PA_VOLUME_NORM; + u->sink->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1; + } + + u->sink->get_volume = sink_get_volume_cb; + u->sink->set_volume = sink_set_volume_cb; + + u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->mixer_path->has_dB ? PA_SINK_DECIBEL_VOLUME : 0); + pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported"); + } + + if (!u->mixer_path->has_mute) { + pa_log_info("Driver does not support hardware mute control, falling back to software mute control."); + } else { u->sink->get_mute = sink_get_mute_cb; u->sink->set_mute = sink_set_mute_cb; u->sink->flags |= PA_SINK_HW_MUTE_CTRL; - } else - pa_log_info("Using software mute control."); + pa_log_info("Using hardware mute control."); + } u->mixer_fdl = pa_alsa_fdlist_new(); @@ -1544,13 +1490,15 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) { return -1; } - snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback); - snd_mixer_elem_set_callback_private(u->mixer_elem, u); + if (u->mixer_path_set) + pa_alsa_path_set_set_callback(u->mixer_path_set, u->mixer_handle, mixer_callback, u); + else + pa_alsa_path_set_callback(u->mixer_path, u->mixer_handle, mixer_callback, u); return 0; } -pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) { +pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) { struct userdata *u = NULL; const char *dev_id = NULL; @@ -1561,7 +1509,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_sink_new_data data; - char *control_device = NULL; + pa_alsa_profile_set *profile_set = NULL; pa_assert(m); pa_assert(ma); @@ -1646,32 +1594,35 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca b = use_mmap; d = use_tsched; - if (profile) { + if (mapping) { if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) { pa_log("device_id= not set"); goto fail; } - if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile( + if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping( dev_id, &u->device_name, &ss, &map, SND_PCM_STREAM_PLAYBACK, &nfrags, &period_frames, tsched_frames, - &b, &d, profile))) + &b, &d, mapping))) goto fail; } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) { + if (!(profile_set = pa_alsa_profile_set_new(NULL, &map))) + goto fail; + if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto( dev_id, &u->device_name, &ss, &map, SND_PCM_STREAM_PLAYBACK, &nfrags, &period_frames, tsched_frames, - &b, &d, &profile))) + &b, &d, profile_set, &mapping))) goto fail; @@ -1685,7 +1636,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca &nfrags, &period_frames, tsched_frames, &b, &d, FALSE))) goto fail; - } pa_assert(u->device_name); @@ -1696,8 +1646,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca goto fail; } - if (profile) - pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name); + if (mapping) + pa_log_info("Selected mapping '%s' (%s).", mapping->description, mapping->name); if (use_mmap && !b) { pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode."); @@ -1723,7 +1673,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca /* ALSA might tweak the sample spec, so recalculate the frame size */ frame_size = pa_frame_size(&ss); - pa_alsa_find_mixer_and_elem(u->pcm_handle, &control_device, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile); + find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB); pa_sink_new_data_init(&data); data.driver = driver; @@ -1733,23 +1683,21 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_channel_map(&data, &map); - pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle, u->mixer_elem); + pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); - if (profile) { - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description); + if (mapping) { + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description); } pa_alsa_init_description(data.proplist); - if (control_device) { - pa_alsa_init_proplist_ctl(data.proplist, control_device); - pa_xfree(control_device); - } + if (u->control_device) + pa_alsa_init_proplist_ctl(data.proplist, u->control_device); if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); @@ -1757,6 +1705,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca goto fail; } + if (u->mixer_path_set) + pa_alsa_add_ports(&data.ports, u->mixer_path_set); + u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY|(u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0)); pa_sink_new_data_done(&data); @@ -1768,6 +1719,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca u->sink->parent.process_msg = sink_process_msg; u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->set_state = sink_set_state_cb; + u->sink->set_port = sink_set_port_cb; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); @@ -1836,6 +1788,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_sink_put(u->sink); + if (profile_set) + pa_alsa_profile_set_free(profile_set); + return u->sink; fail: @@ -1843,6 +1798,9 @@ fail: if (u) userdata_free(u); + if (profile_set) + pa_alsa_profile_set_free(profile_set); + return NULL; } @@ -1871,17 +1829,22 @@ static void userdata_free(struct userdata *u) { if (u->rtpoll) pa_rtpoll_free(u->rtpoll); + if (u->pcm_handle) { + snd_pcm_drop(u->pcm_handle); + snd_pcm_close(u->pcm_handle); + } + if (u->mixer_fdl) pa_alsa_fdlist_free(u->mixer_fdl); + if (u->mixer_path_set) + pa_alsa_path_set_free(u->mixer_path_set); + else if (u->mixer_path) + pa_alsa_path_free(u->mixer_path); + if (u->mixer_handle) snd_mixer_close(u->mixer_handle); - if (u->pcm_handle) { - snd_pcm_drop(u->pcm_handle); - snd_pcm_close(u->pcm_handle); - } - if (u->smoother) pa_smoother_free(u->smoother); @@ -1889,6 +1852,7 @@ static void userdata_free(struct userdata *u) { monitor_done(u); pa_xfree(u->device_name); + pa_xfree(u->control_device); pa_xfree(u); } diff --git a/src/modules/alsa/alsa-sink.h b/src/modules/alsa/alsa-sink.h index bbf64234..b9a4ac2a 100644 --- a/src/modules/alsa/alsa-sink.h +++ b/src/modules/alsa/alsa-sink.h @@ -28,8 +28,9 @@ #include #include "alsa-util.h" +#include "alsa-mixer.h" -pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile); +pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping); void pa_alsa_sink_free(pa_sink *s); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index c176309e..f2e4e234 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -28,10 +28,6 @@ #include -#ifdef HAVE_VALGRIND_MEMCHECK_H -#include -#endif - #include #include #include @@ -81,11 +77,8 @@ struct userdata { pa_alsa_fdlist *mixer_fdl; snd_mixer_t *mixer_handle; - snd_mixer_elem_t *mixer_elem; - long hw_volume_max, hw_volume_min; - long hw_dB_max, hw_dB_min; - pa_bool_t hw_dB_supported:1; - pa_bool_t mixer_seperate_channels:1; + pa_alsa_path_set *mixer_path_set; + pa_alsa_path *mixer_path; pa_cvolume hardware_volume; @@ -102,6 +95,7 @@ struct userdata { unsigned nfragments; char *device_name; + char *control_device; pa_bool_t use_mmap:1, use_tsched:1; @@ -949,239 +943,135 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { return 0; } -static pa_volume_t from_alsa_volume(struct userdata *u, long alsa_vol) { - - return (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / - (double) (u->hw_volume_max - u->hw_volume_min)); -} - -static long to_alsa_volume(struct userdata *u, pa_volume_t vol) { - long alsa_vol; - - alsa_vol = (long) round(((double) vol * (double) (u->hw_volume_max - u->hw_volume_min)) - / PA_VOLUME_NORM) + u->hw_volume_min; - - return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); -} - static void source_get_volume_cb(pa_source *s) { struct userdata *u = s->userdata; - int err; - unsigned i; pa_cvolume r; char t[PA_CVOLUME_SNPRINT_MAX]; pa_assert(u); - pa_assert(u->mixer_elem); - - if (u->mixer_seperate_channels) { - - r.channels = s->sample_spec.channels; - - for (i = 0; i < s->sample_spec.channels; i++) { - long alsa_vol; - - if (u->hw_dB_supported) { - - if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) - goto fail; - -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); -#endif - - r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0); - } else { - - if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) - goto fail; - - r.values[i] = from_alsa_volume(u, alsa_vol); - } - } - - } else { - long alsa_vol; - - if (u->hw_dB_supported) { - - if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) - goto fail; - -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); -#endif - - pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); - - } else { + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); - if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) - goto fail; + if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) + return; - pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol)); - } - } + /* Shift down by the base volume, so that 0dB becomes maximum volume */ + pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume); pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); - if (!pa_cvolume_equal(&u->hardware_volume, &r)) { + if (pa_cvolume_equal(&u->hardware_volume, &r)) + return; - s->virtual_volume = u->hardware_volume = r; + s->virtual_volume = u->hardware_volume = r; - if (u->hw_dB_supported) { - pa_cvolume reset; + if (u->mixer_path->has_dB) { + pa_cvolume reset; - /* Hmm, so the hardware volume changed, let's reset our software volume */ - pa_cvolume_reset(&reset, s->sample_spec.channels); - pa_source_set_soft_volume(s, &reset); - } + /* Hmm, so the hardware volume changed, let's reset our software volume */ + pa_cvolume_reset(&reset, s->sample_spec.channels); + pa_source_set_soft_volume(s, &reset); } - - return; - -fail: - pa_log_error("Unable to read volume: %s", pa_alsa_strerror(err)); } static void source_set_volume_cb(pa_source *s) { struct userdata *u = s->userdata; - int err; - unsigned i; pa_cvolume r; + char t[PA_CVOLUME_SNPRINT_MAX]; pa_assert(u); - pa_assert(u->mixer_elem); - - if (u->mixer_seperate_channels) { - - r.channels = s->sample_spec.channels; - - for (i = 0; i < s->sample_spec.channels; i++) { - long alsa_vol; - pa_volume_t vol; - - vol = s->virtual_volume.values[i]; - - if (u->hw_dB_supported) { - - alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); - alsa_vol += u->hw_dB_max; - alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); - - if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0) - goto fail; - - if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) - goto fail; - -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); -#endif - - r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0); - - } else { - alsa_vol = to_alsa_volume(u, vol); - - if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) - goto fail; - - if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) - goto fail; - - r.values[i] = from_alsa_volume(u, alsa_vol); - } - } - - } else { - pa_volume_t vol; - long alsa_vol; - - vol = pa_cvolume_max(&s->virtual_volume); - - if (u->hw_dB_supported) { - alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); - alsa_vol += u->hw_dB_max; - alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); - - if ((err = snd_mixer_selem_set_capture_dB_all(u->mixer_elem, alsa_vol, 1)) < 0) - goto fail; - - if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) - goto fail; - -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); -#endif - - pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); - - } else { - alsa_vol = to_alsa_volume(u, vol); + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); - if ((err = snd_mixer_selem_set_capture_volume_all(u->mixer_elem, alsa_vol)) < 0) - goto fail; + /* Shift up by the base volume */ + pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume); - if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) - goto fail; + if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) + return; - pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol)); - } - } + /* Shift down by the base volume, so that 0dB becomes maximum volume */ + pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume); u->hardware_volume = r; - if (u->hw_dB_supported) { - char t[PA_CVOLUME_SNPRINT_MAX]; + if (u->mixer_path->has_dB) { /* Match exactly what the user requested by software */ - pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); - } else + } else { + pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); /* We can't match exactly what the user requested, hence let's * at least tell the user about it */ s->virtual_volume = r; - - return; - -fail: - pa_log_error("Unable to set volume: %s", pa_alsa_strerror(err)); + } } static void source_get_mute_cb(pa_source *s) { struct userdata *u = s->userdata; - int err, sw = 0; + pa_bool_t b; pa_assert(u); - pa_assert(u->mixer_elem); + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); - if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) { - pa_log_error("Unable to get switch: %s", pa_alsa_strerror(err)); + if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0) return; - } - s->muted = !sw; + s->muted = b; } static void source_set_mute_cb(pa_source *s) { struct userdata *u = s->userdata; - int err; pa_assert(u); - pa_assert(u->mixer_elem); + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); - if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) { - pa_log_error("Unable to set switch: %s", pa_alsa_strerror(err)); - return; + pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted); +} + +static int source_set_port_cb(pa_source *s, pa_device_port *p) { + struct userdata *u = s->userdata; + pa_alsa_port_data *data; + + pa_assert(u); + pa_assert(p); + pa_assert(u->mixer_handle); + + data = PA_DEVICE_PORT_DATA(p); + + pa_assert_se(u->mixer_path = data->path); + pa_alsa_path_select(u->mixer_path, u->mixer_handle); + + if (u->mixer_path->has_volume && u->mixer_path->has_dB) { + s->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB); + s->n_volume_steps = PA_VOLUME_NORM+1; + + if (u->mixer_path->max_dB > 0.0) + pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(s->base_volume)); + else + pa_log_info("No particular base volume set, fixing to 0 dB"); + } else { + s->base_volume = PA_VOLUME_NORM; + s->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1; } + + if (data->setting) + pa_alsa_setting_select(data->setting, u->mixer_handle); + + if (s->set_mute) + s->set_mute(s); + if (s->set_volume) + s->set_volume(s); + + return 0; } static void source_update_requested_latency_cb(pa_source *s) { @@ -1323,77 +1213,127 @@ static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char pa_xfree(t); } +static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, pa_bool_t ignore_dB) { + + if (!mapping && !element) + return; + + if (!(u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device))) { + pa_log_info("Failed to find a working mixer device."); + return; + } + + if (element) { + + if (!(u->mixer_path = pa_alsa_path_synthesize(element, PA_ALSA_DIRECTION_INPUT))) + goto fail; + + if (pa_alsa_path_probe(u->mixer_path, u->mixer_handle, ignore_dB) < 0) + goto fail; + + pa_log_debug("Probed mixer path %s:", u->mixer_path->name); + pa_alsa_path_dump(u->mixer_path); + } else { + + if (!(u->mixer_path_set = pa_alsa_path_set_new(mapping, PA_ALSA_DIRECTION_INPUT))) + goto fail; + + pa_alsa_path_set_probe(u->mixer_path_set, u->mixer_handle, ignore_dB); + + pa_log_debug("Probed mixer paths:"); + pa_alsa_path_set_dump(u->mixer_path_set); + } + + return; + +fail: + + if (u->mixer_path_set) { + pa_alsa_path_set_free(u->mixer_path_set); + u->mixer_path_set = NULL; + } else if (u->mixer_path) { + pa_alsa_path_free(u->mixer_path); + u->mixer_path = NULL; + } + + if (u->mixer_handle) { + snd_mixer_close(u->mixer_handle); + u->mixer_handle = NULL; + } +} + static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) { pa_assert(u); if (!u->mixer_handle) return 0; - pa_assert(u->mixer_elem); + if (u->source->active_port) { + pa_alsa_port_data *data; - if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) { - pa_bool_t suitable = FALSE; + /* We have a list of supported paths, so let's activate the + * one that has been chosen as active */ - if (snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0) - pa_log_info("Failed to get volume range. Falling back to software volume control."); - else if (u->hw_volume_min >= u->hw_volume_max) - pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", u->hw_volume_min, u->hw_volume_max); - else { - pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max); - suitable = TRUE; - } + data = PA_DEVICE_PORT_DATA(u->source->active_port); + u->mixer_path = data->path; - if (suitable) { - if (ignore_dB || snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0) - pa_log_info("Mixer doesn't support dB information or data is ignored."); - else { -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min)); - VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max)); -#endif + pa_alsa_path_select(data->path, u->mixer_handle); - if (u->hw_dB_min >= u->hw_dB_max) - pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0); - else { - pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0); - u->hw_dB_supported = TRUE; - - if (u->hw_dB_max > 0) { - u->source->base_volume = pa_sw_volume_from_dB(- (double) u->hw_dB_max/100.0); - pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume)); - } else - pa_log_info("No particular base volume set, fixing to 0 dB"); - } - } + if (data->setting) + pa_alsa_setting_select(data->setting, u->mixer_handle); - if (!u->hw_dB_supported && - u->hw_volume_max - u->hw_volume_min < 3) { + } else { - pa_log_info("Device has less than 4 volume levels. Falling back to software volume control."); - suitable = FALSE; - } - } + if (!u->mixer_path && u->mixer_path_set) + u->mixer_path = u->mixer_path_set->paths; - if (suitable) { - u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &u->source->channel_map, u->mixer_map, FALSE) >= 0; + if (u->mixer_path) { + /* Hmm, we have only a single path, then let's activate it */ - u->source->get_volume = source_get_volume_cb; - u->source->set_volume = source_set_volume_cb; - u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0); - pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported"); + pa_alsa_path_select(u->mixer_path, u->mixer_handle); - if (!u->hw_dB_supported) - u->source->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1; + if (u->mixer_path->settings) + pa_alsa_setting_select(u->mixer_path->settings, u->mixer_handle); } else - pa_log_info("Using software volume control."); + return 0; + } + + if (!u->mixer_path->has_volume) + pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); + else { + + if (u->mixer_path->has_dB) { + pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB); + + u->source->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB); + u->source->n_volume_steps = PA_VOLUME_NORM+1; + + if (u->mixer_path->max_dB > 0.0) + pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume)); + else + pa_log_info("No particular base volume set, fixing to 0 dB"); + + } else { + pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume); + u->source->base_volume = PA_VOLUME_NORM; + u->source->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1; + } + + u->source->get_volume = source_get_volume_cb; + u->source->set_volume = source_set_volume_cb; + + u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->mixer_path->has_dB ? PA_SOURCE_DECIBEL_VOLUME : 0); + pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported"); } - if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) { + if (!u->mixer_path->has_mute) { + pa_log_info("Driver does not support hardware mute control, falling back to software mute control."); + } else { u->source->get_mute = source_get_mute_cb; u->source->set_mute = source_set_mute_cb; u->source->flags |= PA_SOURCE_HW_MUTE_CTRL; - } else - pa_log_info("Using software mute control."); + pa_log_info("Using hardware mute control."); + } u->mixer_fdl = pa_alsa_fdlist_new(); @@ -1402,13 +1342,15 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) { return -1; } - snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback); - snd_mixer_elem_set_callback_private(u->mixer_elem, u); + if (u->mixer_path_set) + pa_alsa_path_set_set_callback(u->mixer_path_set, u->mixer_handle, mixer_callback, u); + else + pa_alsa_path_set_callback(u->mixer_path, u->mixer_handle, mixer_callback, u); return 0; } -pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) { +pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) { struct userdata *u = NULL; const char *dev_id = NULL; @@ -1419,7 +1361,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_source_new_data data; - char *control_device = NULL; + pa_alsa_profile_set *profile_set = NULL; pa_assert(m); pa_assert(ma); @@ -1480,7 +1422,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p u->use_tsched = use_tsched; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); - u->alsa_rtpoll_item = NULL; u->smoother = pa_smoother_new( DEFAULT_TSCHED_WATERMARK_USEC*2, @@ -1504,31 +1445,34 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p b = use_mmap; d = use_tsched; - if (profile) { + if (mapping) { if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) { pa_log("device_id= not set"); goto fail; } - if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile( + if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping( dev_id, &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, &nfrags, &period_frames, tsched_frames, - &b, &d, profile))) + &b, &d, mapping))) goto fail; } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) { + if (!(profile_set = pa_alsa_profile_set_new(NULL, &map))) + goto fail; + if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto( dev_id, &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, &nfrags, &period_frames, tsched_frames, - &b, &d, &profile))) + &b, &d, profile_set, &mapping))) goto fail; } else { @@ -1551,8 +1495,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p goto fail; } - if (profile) - pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name); + if (mapping) + pa_log_info("Selected mapping '%s' (%s).", mapping->description, mapping->name); if (use_mmap && !b) { pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode."); @@ -1578,7 +1522,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p /* ALSA might tweak the sample spec, so recalculate the frame size */ frame_size = pa_frame_size(&ss); - pa_alsa_find_mixer_and_elem(u->pcm_handle, &control_device, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile); + find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB); pa_source_new_data_init(&data); data.driver = driver; @@ -1588,23 +1532,21 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_source_new_data_set_sample_spec(&data, &ss); pa_source_new_data_set_channel_map(&data, &map); - pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle, u->mixer_elem); + pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); - if (profile) { - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description); + if (mapping) { + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description); } pa_alsa_init_description(data.proplist); - if (control_device) { - pa_alsa_init_proplist_ctl(data.proplist, control_device); - pa_xfree(control_device); - } + if (u->control_device) + pa_alsa_init_proplist_ctl(data.proplist, u->control_device); if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); @@ -1612,6 +1554,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p goto fail; } + if (u->mixer_path_set) + pa_alsa_add_ports(&data.ports, u->mixer_path_set); + u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|(u->use_tsched ? PA_SOURCE_DYNAMIC_LATENCY : 0)); pa_source_new_data_done(&data); @@ -1623,6 +1568,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p u->source->parent.process_msg = source_process_msg; u->source->update_requested_latency = source_update_requested_latency_cb; u->source->set_state = source_set_state_cb; + u->source->set_port = source_set_port_cb; u->source->userdata = u; pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); @@ -1687,6 +1633,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_source_put(u->source); + if (profile_set) + pa_alsa_profile_set_free(profile_set); + return u->source; fail: @@ -1694,6 +1643,9 @@ fail: if (u) userdata_free(u); + if (profile_set) + pa_alsa_profile_set_free(profile_set); + return NULL; } @@ -1719,17 +1671,22 @@ static void userdata_free(struct userdata *u) { if (u->rtpoll) pa_rtpoll_free(u->rtpoll); + if (u->pcm_handle) { + snd_pcm_drop(u->pcm_handle); + snd_pcm_close(u->pcm_handle); + } + if (u->mixer_fdl) pa_alsa_fdlist_free(u->mixer_fdl); + if (u->mixer_path_set) + pa_alsa_path_set_free(u->mixer_path_set); + else if (u->mixer_path) + pa_alsa_path_free(u->mixer_path); + if (u->mixer_handle) snd_mixer_close(u->mixer_handle); - if (u->pcm_handle) { - snd_pcm_drop(u->pcm_handle); - snd_pcm_close(u->pcm_handle); - } - if (u->smoother) pa_smoother_free(u->smoother); @@ -1737,6 +1694,7 @@ static void userdata_free(struct userdata *u) { monitor_done(u); pa_xfree(u->device_name); + pa_xfree(u->control_device); pa_xfree(u); } diff --git a/src/modules/alsa/alsa-source.h b/src/modules/alsa/alsa-source.h index 9cbb0e17..5d9409e2 100644 --- a/src/modules/alsa/alsa-source.h +++ b/src/modules/alsa/alsa-source.h @@ -29,7 +29,7 @@ #include "alsa-util.h" -pa_source* pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile); +pa_source* pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping); void pa_alsa_source_free(pa_source *s); diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index b2736252..d117ccd1 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -42,8 +42,10 @@ #include #include #include +#include #include "alsa-util.h" +#include "alsa-mixer.h" #ifdef HAVE_HAL #include "hal-util.h" @@ -53,182 +55,6 @@ #include "udev-util.h" #endif -struct pa_alsa_fdlist { - unsigned num_fds; - struct pollfd *fds; - /* This is a temporary buffer used to avoid lots of mallocs */ - struct pollfd *work_fds; - - snd_mixer_t *mixer; - - pa_mainloop_api *m; - pa_defer_event *defer; - pa_io_event **ios; - - pa_bool_t polled; - - void (*cb)(void *userdata); - void *userdata; -}; - -static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { - - struct pa_alsa_fdlist *fdl = userdata; - int err; - unsigned i; - unsigned short revents; - - pa_assert(a); - pa_assert(fdl); - pa_assert(fdl->mixer); - pa_assert(fdl->fds); - pa_assert(fdl->work_fds); - - if (fdl->polled) - return; - - fdl->polled = TRUE; - - memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds); - - for (i = 0; i < fdl->num_fds; i++) { - if (e == fdl->ios[i]) { - if (events & PA_IO_EVENT_INPUT) - fdl->work_fds[i].revents |= POLLIN; - if (events & PA_IO_EVENT_OUTPUT) - fdl->work_fds[i].revents |= POLLOUT; - if (events & PA_IO_EVENT_ERROR) - fdl->work_fds[i].revents |= POLLERR; - if (events & PA_IO_EVENT_HANGUP) - fdl->work_fds[i].revents |= POLLHUP; - break; - } - } - - pa_assert(i != fdl->num_fds); - - if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) { - pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err)); - return; - } - - a->defer_enable(fdl->defer, 1); - - if (revents) - snd_mixer_handle_events(fdl->mixer); -} - -static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) { - struct pa_alsa_fdlist *fdl = userdata; - unsigned num_fds, i; - int err, n; - struct pollfd *temp; - - pa_assert(a); - pa_assert(fdl); - pa_assert(fdl->mixer); - - a->defer_enable(fdl->defer, 0); - - if ((n = snd_mixer_poll_descriptors_count(fdl->mixer)) < 0) { - pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n)); - return; - } - num_fds = (unsigned) n; - - if (num_fds != fdl->num_fds) { - if (fdl->fds) - pa_xfree(fdl->fds); - if (fdl->work_fds) - pa_xfree(fdl->work_fds); - fdl->fds = pa_xnew0(struct pollfd, num_fds); - fdl->work_fds = pa_xnew(struct pollfd, num_fds); - } - - memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds); - - if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) { - pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err)); - return; - } - - fdl->polled = FALSE; - - if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0) - return; - - if (fdl->ios) { - for (i = 0; i < fdl->num_fds; i++) - a->io_free(fdl->ios[i]); - - if (num_fds != fdl->num_fds) { - pa_xfree(fdl->ios); - fdl->ios = NULL; - } - } - - if (!fdl->ios) - fdl->ios = pa_xnew(pa_io_event*, num_fds); - - /* Swap pointers */ - temp = fdl->work_fds; - fdl->work_fds = fdl->fds; - fdl->fds = temp; - - fdl->num_fds = num_fds; - - for (i = 0;i < num_fds;i++) - fdl->ios[i] = a->io_new(a, fdl->fds[i].fd, - ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) | - ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0), - io_cb, fdl); -} - -struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) { - struct pa_alsa_fdlist *fdl; - - fdl = pa_xnew0(struct pa_alsa_fdlist, 1); - - return fdl; -} - -void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) { - pa_assert(fdl); - - if (fdl->defer) { - pa_assert(fdl->m); - fdl->m->defer_free(fdl->defer); - } - - if (fdl->ios) { - unsigned i; - pa_assert(fdl->m); - for (i = 0; i < fdl->num_fds; i++) - fdl->m->io_free(fdl->ios[i]); - pa_xfree(fdl->ios); - } - - if (fdl->fds) - pa_xfree(fdl->fds); - if (fdl->work_fds) - pa_xfree(fdl->work_fds); - - pa_xfree(fdl); -} - -int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) { - pa_assert(fdl); - pa_assert(mixer_handle); - pa_assert(m); - pa_assert(!fdl->m); - - fdl->mixer = mixer_handle; - fdl->m = m; - fdl->defer = m->defer_new(m, defer_cb, fdl); - - return 0; -} - static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_sample_format_t *f) { static const snd_pcm_format_t format_trans[] = { @@ -260,11 +86,11 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s PA_SAMPLE_S16RE, PA_SAMPLE_ALAW, PA_SAMPLE_ULAW, - PA_SAMPLE_U8, - PA_SAMPLE_INVALID + PA_SAMPLE_U8 }; - int i, ret; + unsigned i; + int ret; pa_assert(pcm_handle); pa_assert(f); @@ -276,7 +102,6 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s snd_pcm_format_description(format_trans[*f]), pa_alsa_strerror(ret)); - if (*f == PA_SAMPLE_FLOAT32BE) *f = PA_SAMPLE_FLOAT32LE; else if (*f == PA_SAMPLE_FLOAT32LE) @@ -309,7 +134,7 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s try_auto: - for (i = 0; try_order[i] != PA_SAMPLE_INVALID; i++) { + for (i = 0; i < PA_ELEMENTSOF(try_order); i++) { *f = try_order[i]; if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0) @@ -393,12 +218,12 @@ int pa_alsa_set_hw_params( if (require_exact_channel_number) { if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0) { - pa_log_debug("snd_pcm_hw_params_set_channels() failed: %s", pa_alsa_strerror(ret)); + pa_log_debug("snd_pcm_hw_params_set_channels(%u) failed: %s", c, pa_alsa_strerror(ret)); goto finish; } } else { if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0) { - pa_log_debug("snd_pcm_hw_params_set_channels_near() failed: %s", pa_alsa_strerror(ret)); + pa_log_debug("snd_pcm_hw_params_set_channels_near(%u) failed: %s", c, pa_alsa_strerror(ret)); goto finish; } } @@ -415,10 +240,12 @@ int pa_alsa_set_hw_params( tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate); if (_use_tsched) { - snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t buffer_size = 0; - pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0); - pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r); + if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size)) < 0) + pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret)); + else + pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r); _period_size = tsched_size; _periods = 1; @@ -464,12 +291,16 @@ int pa_alsa_set_hw_params( if (ss->format != f) pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f)); - if ((ret = snd_pcm_prepare(pcm_handle)) < 0) + if ((ret = snd_pcm_prepare(pcm_handle)) < 0) { + pa_log_info("snd_pcm_prepare() failed: %s", pa_alsa_strerror(ret)); goto finish; + } if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 || - (ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0) + (ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0) { + pa_log_info("snd_pcm_hw_params_get_period{s|_size}() failed: %s", pa_alsa_strerror(ret)); goto finish; + } /* If the sample rate deviates too much, we need to resample */ if (r < ss->rate*.95 || r > ss->rate*1.05) @@ -553,167 +384,6 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) { return 0; } -static const struct pa_alsa_profile_info device_table[] = { - {{ 1, { PA_CHANNEL_POSITION_MONO }}, - "hw", NULL, - N_("Analog Mono"), - "analog-mono", - 1, - "Master", "PCM", - "Capture", "Mic" }, - - {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }}, - "front", "hw", - N_("Analog Stereo"), - "analog-stereo", - 10, - "Master", "PCM", - "Capture", "Mic" }, - - {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }}, - "iec958", NULL, - N_("Digital Stereo (IEC958)"), - "iec958-stereo", - 5, - "IEC958", NULL, - "IEC958 In", NULL }, - - {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }}, - "hdmi", NULL, - N_("Digital Stereo (HDMI)"), - "hdmi-stereo", - 4, - "IEC958", NULL, - "IEC958 In", NULL }, - - {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, - "surround40", NULL, - N_("Analog Surround 4.0"), - "analog-surround-40", - 7, - "Master", "PCM", - "Capture", "Mic" }, - - {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, - "a52", NULL, - N_("Digital Surround 4.0 (IEC958/AC3)"), - "iec958-ac3-surround-40", - 2, - "Master", "PCM", - "Capture", "Mic" }, - - {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, - PA_CHANNEL_POSITION_LFE }}, - "surround41", NULL, - N_("Analog Surround 4.1"), - "analog-surround-41", - 7, - "Master", "PCM", - "Capture", "Mic" }, - - {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, - PA_CHANNEL_POSITION_CENTER }}, - "surround50", NULL, - N_("Analog Surround 5.0"), - "analog-surround-50", - 7, - "Master", "PCM", - "Capture", "Mic" }, - - {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, - PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }}, - "surround51", NULL, - N_("Analog Surround 5.1"), - "analog-surround-51", - 8, - "Master", "PCM", - "Capture", "Mic" }, - - {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, - PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE}}, - "a52", NULL, - N_("Digital Surround 5.1 (IEC958/AC3)"), - "iec958-ac3-surround-51", - 3, - "IEC958", NULL, - "IEC958 In", NULL }, - - {{ 8, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, - PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE, - PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }}, - "surround71", NULL, - N_("Analog Surround 7.1"), - "analog-surround-71", - 7, - "Master", "PCM", - "Capture", "Mic" }, - - {{ 0, { 0 }}, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL } -}; - -static snd_pcm_t *open_by_device_string_with_fallback( - const char *prefix, - const char *prefix_fallback, - const char *dev_id, - char **dev, - pa_sample_spec *ss, - pa_channel_map* map, - int mode, - uint32_t *nfrags, - snd_pcm_uframes_t *period_size, - snd_pcm_uframes_t tsched_size, - pa_bool_t *use_mmap, - pa_bool_t *use_tsched, - pa_bool_t require_exact_channel_number) { - - snd_pcm_t *pcm_handle; - char *d; - - d = pa_sprintf_malloc("%s:%s", prefix, dev_id); - - pcm_handle = pa_alsa_open_by_device_string( - d, - dev, - ss, - map, - mode, - nfrags, - period_size, - tsched_size, - use_mmap, - use_tsched, - require_exact_channel_number); - pa_xfree(d); - - if (!pcm_handle && prefix_fallback) { - - d = pa_sprintf_malloc("%s:%s", prefix_fallback, dev_id); - - pcm_handle = pa_alsa_open_by_device_string( - d, - dev, - ss, - map, - mode, - nfrags, - period_size, - tsched_size, - use_mmap, - use_tsched, - require_exact_channel_number); - pa_xfree(d); - } - - return pcm_handle; -} - snd_pcm_t *pa_alsa_open_by_device_id_auto( const char *dev_id, char **dev, @@ -725,12 +395,13 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, - const pa_alsa_profile_info **profile) { + pa_alsa_profile_set *ps, + pa_alsa_mapping **mapping) { - int i; - int direction = 1; char *d; snd_pcm_t *pcm_handle; + void *state; + pa_alsa_mapping *m; pa_assert(dev_id); pa_assert(dev); @@ -738,113 +409,82 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( pa_assert(map); pa_assert(nfrags); pa_assert(period_size); + pa_assert(ps); /* First we try to find a device string with a superset of the - * requested channel map and open it without the plug: prefix. We - * iterate through our device table from top to bottom and take - * the first that matches. If we didn't find a working device that - * way, we iterate backwards, and check all devices that do not - * provide a superset of the requested channel map.*/ - - i = 0; - for (;;) { - - if ((direction > 0) == pa_channel_map_superset(&device_table[i].map, map)) { - pa_sample_spec try_ss; + * requested channel map. We iterate through our device table from + * top to bottom and take the first that matches. If we didn't + * find a working device that way, we iterate backwards, and check + * all devices that do not provide a superset of the requested + * channel map.*/ - pa_log_debug("Checking for %s (%s)", device_table[i].name, device_table[i].alsa_name); + PA_HASHMAP_FOREACH(m, ps->mappings, state) { + if (!pa_channel_map_superset(&m->channel_map, map)) + continue; - try_ss.channels = device_table[i].map.channels; - try_ss.rate = ss->rate; - try_ss.format = ss->format; + pa_log_debug("Checking for superset %s (%s)", m->name, m->device_strings[0]); - pcm_handle = open_by_device_string_with_fallback( - device_table[i].alsa_name, - device_table[i].alsa_name_fallback, - dev_id, - dev, - &try_ss, - map, - mode, - nfrags, - period_size, - tsched_size, - use_mmap, - use_tsched, - TRUE); - - if (pcm_handle) { - - *ss = try_ss; - *map = device_table[i].map; - pa_assert(map->channels == ss->channels); - - if (profile) - *profile = &device_table[i]; - - return pcm_handle; - } + pcm_handle = pa_alsa_open_by_device_id_mapping( + dev_id, + dev, + ss, + map, + mode, + nfrags, + period_size, + tsched_size, + use_mmap, + use_tsched, + m); - } + if (pcm_handle) { + if (mapping) + *mapping = m; - if (direction > 0) { - if (!device_table[i+1].alsa_name) { - /* OK, so we are at the end of our list. Let's turn - * back. */ - direction = -1; - } else { - /* We are not at the end of the list, so let's simply - * try the next entry */ - i++; - } + return pcm_handle; } + } - if (direction < 0) { - - if (device_table[i+1].alsa_name && - device_table[i].map.channels == device_table[i+1].map.channels) { - - /* OK, the next entry has the same number of channels, - * let's try it */ - i++; + PA_HASHMAP_FOREACH_BACKWARDS(m, ps->mappings, state) { + if (pa_channel_map_superset(&m->channel_map, map)) + continue; - } else { - /* Hmm, so the next entry does not have the same - * number of channels, so let's go backwards until we - * find the next entry with a different number of - * channels */ + pa_log_debug("Checking for subset %s (%s)", m->name, m->device_strings[0]); - for (i--; i >= 0; i--) - if (device_table[i].map.channels != device_table[i+1].map.channels) - break; + pcm_handle = pa_alsa_open_by_device_id_mapping( + dev_id, + dev, + ss, + map, + mode, + nfrags, + period_size, + tsched_size, + use_mmap, + use_tsched, + m); - /* Hmm, there is no entry with a different number of - * entries, then we're done */ - if (i < 0) - break; + if (pcm_handle) { + if (mapping) + *mapping = m; - /* OK, now lets find go back as long as we have the same number of channels */ - for (; i > 0; i--) - if (device_table[i].map.channels != device_table[i-1].map.channels) - break; - } + return pcm_handle; } } - /* OK, we didn't find any good device, so let's try the raw plughw: stuff */ - + /* OK, we didn't find any good device, so let's try the raw hw: stuff */ d = pa_sprintf_malloc("hw:%s", dev_id); pa_log_debug("Trying %s as last resort...", d); pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE); pa_xfree(d); - if (pcm_handle && profile) - *profile = NULL; + if (pcm_handle && mapping) + *mapping = NULL; return pcm_handle; } -snd_pcm_t *pa_alsa_open_by_device_id_profile( +snd_pcm_t *pa_alsa_open_by_device_id_mapping( const char *dev_id, char **dev, pa_sample_spec *ss, @@ -855,10 +495,11 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile( snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, - const pa_alsa_profile_info *profile) { + pa_alsa_mapping *m) { snd_pcm_t *pcm_handle; pa_sample_spec try_ss; + pa_channel_map try_map; pa_assert(dev_id); pa_assert(dev); @@ -866,19 +507,19 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile( pa_assert(map); pa_assert(nfrags); pa_assert(period_size); - pa_assert(profile); + pa_assert(m); - try_ss.channels = profile->map.channels; + try_ss.channels = m->channel_map.channels; try_ss.rate = ss->rate; try_ss.format = ss->format; + try_map = m->channel_map; - pcm_handle = open_by_device_string_with_fallback( - profile->alsa_name, - profile->alsa_name_fallback, + pcm_handle = pa_alsa_open_by_device_string_strv( + m->device_strings, dev_id, dev, &try_ss, - map, + &try_map, mode, nfrags, period_size, @@ -891,7 +532,7 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile( return NULL; *ss = try_ss; - *map = profile->map; + *map = try_map; pa_assert(map->channels == ss->channels); return pcm_handle; @@ -924,14 +565,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( for (;;) { pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with"); - /* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <= - * 1.0.17a would then ignore the SND_PCM_NO_xxx flags. Instead - * we enable nonblock mode afterwards via - * snd_pcm_nonblock(). Also see - * http://mailman.alsa-project.org/pipermail/alsa-devel/2008-August/010258.html */ - if ((err = snd_pcm_open(&pcm_handle, d, mode, - /*SND_PCM_NONBLOCK|*/ + SND_PCM_NONBLOCK| SND_PCM_NO_AUTO_RESAMPLE| SND_PCM_NO_AUTO_CHANNELS| (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) { @@ -951,7 +586,6 @@ snd_pcm_t *pa_alsa_open_by_device_string( } /* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */ - if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) { char *t; @@ -988,442 +622,48 @@ fail: return NULL; } -int pa_alsa_probe_profiles( +snd_pcm_t *pa_alsa_open_by_device_string_strv( + char **prefix, const char *dev_id, - const pa_sample_spec *ss, - void (*cb)(const pa_alsa_profile_info *sink, const pa_alsa_profile_info *source, void *userdata), - void *userdata) { - - const pa_alsa_profile_info *i; - - pa_assert(dev_id); - pa_assert(ss); - pa_assert(cb); - - /* We try each combination of playback/capture. We also try to - * open only for capture resp. only for sink. Don't get confused - * by the trailing entry in device_table we use for this! */ - - for (i = device_table; i < device_table + PA_ELEMENTSOF(device_table); i++) { - const pa_alsa_profile_info *j; - snd_pcm_t *pcm_i = NULL; - - if (i->alsa_name) { - pa_sample_spec try_ss; - pa_channel_map try_map; - - pa_log_debug("Checking for playback on %s (%s)", i->name, i->alsa_name); - - try_ss = *ss; - try_ss.channels = i->map.channels; - try_map = i->map; - - pcm_i = open_by_device_string_with_fallback( - i->alsa_name, - i->alsa_name_fallback, - dev_id, - NULL, - &try_ss, &try_map, - SND_PCM_STREAM_PLAYBACK, - NULL, NULL, 0, NULL, NULL, - TRUE); - - if (!pcm_i) - continue; - } - - for (j = device_table; j < device_table + PA_ELEMENTSOF(device_table); j++) { - snd_pcm_t *pcm_j = NULL; - - if (j->alsa_name) { - pa_sample_spec try_ss; - pa_channel_map try_map; - - pa_log_debug("Checking for capture on %s (%s)", j->name, j->alsa_name); - - try_ss = *ss; - try_ss.channels = j->map.channels; - try_map = j->map; - - pcm_j = open_by_device_string_with_fallback( - j->alsa_name, - j->alsa_name_fallback, - dev_id, - NULL, - &try_ss, &try_map, - SND_PCM_STREAM_CAPTURE, - NULL, NULL, 0, NULL, NULL, - TRUE); - - if (!pcm_j) - continue; - } - - if (pcm_j) - snd_pcm_close(pcm_j); - - if (i->alsa_name || j->alsa_name) - cb(i->alsa_name ? i : NULL, - j->alsa_name ? j : NULL, userdata); - } - - if (pcm_i) - snd_pcm_close(pcm_i); - } - - return TRUE; -} - -int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) { - int err; - - pa_assert(mixer); - pa_assert(dev); - - if ((err = snd_mixer_attach(mixer, dev)) < 0) { - pa_log_info("Unable to attach to mixer %s: %s", dev, pa_alsa_strerror(err)); - return -1; - } - - if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) { - pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err)); - return -1; - } - - if ((err = snd_mixer_load(mixer)) < 0) { - pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err)); - return -1; - } - - pa_log_info("Successfully attached to mixer '%s'", dev); - - return 0; -} - -static pa_bool_t elem_has_volume(snd_mixer_elem_t *elem, pa_bool_t playback) { - pa_assert(elem); - - if (playback && snd_mixer_selem_has_playback_volume(elem)) - return TRUE; - - if (!playback && snd_mixer_selem_has_capture_volume(elem)) - return TRUE; - - return FALSE; -} - -static pa_bool_t elem_has_switch(snd_mixer_elem_t *elem, pa_bool_t playback) { - pa_assert(elem); - - if (playback && snd_mixer_selem_has_playback_switch(elem)) - return TRUE; - - if (!playback && snd_mixer_selem_has_capture_switch(elem)) - return TRUE; - - return FALSE; -} - -snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback) { - snd_mixer_elem_t *elem = NULL, *fallback_elem = NULL; - snd_mixer_selem_id_t *sid = NULL; - - snd_mixer_selem_id_alloca(&sid); - - pa_assert(mixer); - pa_assert(name); - - snd_mixer_selem_id_set_name(sid, name); - snd_mixer_selem_id_set_index(sid, 0); - - if ((elem = snd_mixer_find_selem(mixer, sid))) { - - if (elem_has_volume(elem, playback) && - elem_has_switch(elem, playback)) - goto success; - - if (!elem_has_volume(elem, playback) && - !elem_has_switch(elem, playback)) - elem = NULL; - } - - pa_log_info("Cannot find mixer control \"%s\" or mixer control is no combination of switch/volume.", snd_mixer_selem_id_get_name(sid)); - - if (fallback) { - snd_mixer_selem_id_set_name(sid, fallback); - snd_mixer_selem_id_set_index(sid, 0); - - if ((fallback_elem = snd_mixer_find_selem(mixer, sid))) { - - if (elem_has_volume(fallback_elem, playback) && - elem_has_switch(fallback_elem, playback)) { - elem = fallback_elem; - goto success; - } - - if (!elem_has_volume(fallback_elem, playback) && - !elem_has_switch(fallback_elem, playback)) - fallback_elem = NULL; - } - - pa_log_info("Cannot find fallback mixer control \"%s\" or mixer control is no combination of switch/volume.", snd_mixer_selem_id_get_name(sid)); - } - - if (elem && fallback_elem) { - - /* Hmm, so we have both elements, but neither has both mute - * and volume. Let's prefer the one with the volume */ - - if (elem_has_volume(elem, playback)) - goto success; - - if (elem_has_volume(fallback_elem, playback)) { - elem = fallback_elem; - goto success; - } - } - - if (!elem && fallback_elem) - elem = fallback_elem; - -success: - - if (elem) - pa_log_info("Using mixer control \"%s\".", snd_mixer_selem_id_get_name(sid)); - - return elem; -} - -int pa_alsa_find_mixer_and_elem( - snd_pcm_t *pcm, - char **ctl_device, - snd_mixer_t **_m, - snd_mixer_elem_t **_e, - const char *control_name, - const pa_alsa_profile_info *profile) { - - int err; - snd_mixer_t *m; - snd_mixer_elem_t *e; - pa_bool_t found = FALSE; - const char *dev; - - pa_assert(pcm); - pa_assert(_m); - pa_assert(_e); - - if (control_name && *control_name == 0) { - pa_log_debug("Hardware mixer usage disabled because empty control name passed"); - return -1; - } - - if ((err = snd_mixer_open(&m, 0)) < 0) { - pa_log("Error opening mixer: %s", pa_alsa_strerror(err)); - return -1; - } - - /* First, try by name */ - if ((dev = snd_pcm_name(pcm))) - if (pa_alsa_prepare_mixer(m, dev) >= 0) { - found = TRUE; - - if (ctl_device) - *ctl_device = pa_xstrdup(dev); - } - - /* Then, try by card index */ - if (!found) { - snd_pcm_info_t* info; - snd_pcm_info_alloca(&info); - - if (snd_pcm_info(pcm, info) >= 0) { - char *md; - int card_idx; - - if ((card_idx = snd_pcm_info_get_card(info)) >= 0) { - - md = pa_sprintf_malloc("hw:%i", card_idx); - - if (!dev || !pa_streq(dev, md)) - if (pa_alsa_prepare_mixer(m, md) >= 0) { - found = TRUE; - - if (ctl_device) { - *ctl_device = md; - md = NULL; - } - } - - pa_xfree(md); - } - } - } - - if (!found) { - snd_mixer_close(m); - return -1; - } - - switch (snd_pcm_stream(pcm)) { - - case SND_PCM_STREAM_PLAYBACK: - if (control_name) - e = pa_alsa_find_elem(m, control_name, NULL, TRUE); - else if (profile) - e = pa_alsa_find_elem(m, profile->playback_control_name, profile->playback_control_fallback, TRUE); - else - e = pa_alsa_find_elem(m, "Master", "PCM", TRUE); - break; - - case SND_PCM_STREAM_CAPTURE: - if (control_name) - e = pa_alsa_find_elem(m, control_name, NULL, FALSE); - else if (profile) - e = pa_alsa_find_elem(m, profile->record_control_name, profile->record_control_fallback, FALSE); - else - e = pa_alsa_find_elem(m, "Capture", "Mic", FALSE); - break; - - default: - pa_assert_not_reached(); - } - - if (!e) { - if (ctl_device) { - pa_xfree(*ctl_device); - *ctl_device = NULL; - } - - snd_mixer_close(m); - return -1; - } - - pa_assert(e && m); - - *_m = m; - *_e = e; - - return 0; -} - -static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = { - [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */ - - [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER, - [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT, - [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT, - - [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER, - [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT, - [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT, - - [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER, - - [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN, - - [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT, - [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT, - - [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN, - - [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN, - - [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN, - - [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN -}; - - -int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback) { - unsigned i; - pa_bool_t alsa_channel_used[SND_MIXER_SCHN_LAST]; - pa_bool_t mono_used = FALSE; - - pa_assert(elem); - pa_assert(channel_map); - pa_assert(mixer_map); - - memset(&alsa_channel_used, 0, sizeof(alsa_channel_used)); - - if (channel_map->channels > 1 && - ((playback && snd_mixer_selem_has_playback_volume_joined(elem)) || - (!playback && snd_mixer_selem_has_capture_volume_joined(elem)))) { - pa_log_info("ALSA device lacks independant volume controls for each channel."); - return -1; - } - - for (i = 0; i < channel_map->channels; i++) { - snd_mixer_selem_channel_id_t id; - pa_bool_t is_mono; + char **dev, + pa_sample_spec *ss, + pa_channel_map* map, + int mode, + uint32_t *nfrags, + snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t tsched_size, + pa_bool_t *use_mmap, + pa_bool_t *use_tsched, + pa_bool_t require_exact_channel_number) { - is_mono = channel_map->map[i] == PA_CHANNEL_POSITION_MONO; - id = alsa_channel_ids[channel_map->map[i]]; + snd_pcm_t *pcm_handle; + char **i; - if (!is_mono && id == SND_MIXER_SCHN_UNKNOWN) { - pa_log_info("Configured channel map contains channel '%s' that is unknown to the ALSA mixer.", pa_channel_position_to_string(channel_map->map[i])); - return -1; - } + for (i = prefix; *i; i++) { + char *d; - if ((is_mono && mono_used) || (!is_mono && alsa_channel_used[id])) { - pa_log_info("Channel map has duplicate channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i])); - return -1; - } + d = pa_sprintf_malloc("%s:%s", *i, dev_id); - if ((playback && (!snd_mixer_selem_has_playback_channel(elem, id) || (is_mono && !snd_mixer_selem_is_playback_mono(elem)))) || - (!playback && (!snd_mixer_selem_has_capture_channel(elem, id) || (is_mono && !snd_mixer_selem_is_capture_mono(elem))))) { + pcm_handle = pa_alsa_open_by_device_string( + d, + dev, + ss, + map, + mode, + nfrags, + period_size, + tsched_size, + use_mmap, + use_tsched, + require_exact_channel_number); - pa_log_info("ALSA device lacks separate volumes control for channel '%s'", pa_channel_position_to_string(channel_map->map[i])); - return -1; - } + pa_xfree(d); - if (is_mono) { - mixer_map[i] = SND_MIXER_SCHN_MONO; - mono_used = TRUE; - } else { - mixer_map[i] = id; - alsa_channel_used[id] = TRUE; - } + if (pcm_handle) + return pcm_handle; } - pa_log_info("All %u channels can be mapped to mixer channels.", channel_map->channels); - - return 0; + return NULL; } void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm) { @@ -1449,24 +689,33 @@ void pa_alsa_dump_status(snd_pcm_t *pcm) { int err; snd_output_t *out; snd_pcm_status_t *status; + char *s = NULL; pa_assert(pcm); snd_pcm_status_alloca(&status); - pa_assert_se(snd_output_buffer_open(&out) == 0); + if ((err = snd_output_buffer_open(&out)) < 0) { + pa_log_debug("snd_output_buffer_open() failed: %s", pa_cstrerror(err)); + return; + } - pa_assert_se(snd_pcm_status(pcm, status) == 0); + if ((err = snd_pcm_status(pcm, status)) < 0) { + pa_log_debug("snd_pcm_status() failed: %s", pa_cstrerror(err)); + goto finish; + } - if ((err = snd_pcm_status_dump(status, out)) < 0) + if ((err = snd_pcm_status_dump(status, out)) < 0) { pa_log_debug("snd_pcm_dump(): %s", pa_alsa_strerror(err)); - else { - char *s = NULL; - snd_output_buffer_string(out, &s); - pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s)); + goto finish; } - pa_assert_se(snd_output_close(out) == 0); + snd_output_buffer_string(out, &s); + pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s)); + +finish: + + snd_output_close(out); } static void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt,...) { @@ -1546,7 +795,7 @@ void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card) { } #ifdef HAVE_UDEV - pa_udev_get_info(c, p, card); + pa_udev_get_info(card, p); #endif #ifdef HAVE_HAL @@ -1583,16 +832,14 @@ void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t * pa_proplist_sets(p, PA_PROP_DEVICE_API, "alsa"); - class = snd_pcm_info_get_class(pcm_info); - if (class <= SND_PCM_CLASS_LAST) { + if ((class = snd_pcm_info_get_class(pcm_info)) <= SND_PCM_CLASS_LAST) { if (class_table[class]) pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, class_table[class]); if (alsa_class_table[class]) pa_proplist_sets(p, "alsa.class", alsa_class_table[class]); } - subclass = snd_pcm_info_get_subclass(pcm_info); - if (subclass <= SND_PCM_SUBCLASS_LAST) + if ((subclass = snd_pcm_info_get_subclass(pcm_info)) <= SND_PCM_SUBCLASS_LAST) if (alsa_subclass_table[subclass]) pa_proplist_sets(p, "alsa.subclass", alsa_subclass_table[subclass]); @@ -1612,7 +859,7 @@ void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t * pa_alsa_init_proplist_card(c, p, card); } -void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm, snd_mixer_elem_t *elem) { +void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm) { snd_pcm_hw_params_t *hwparams; snd_pcm_info_t *info; int bits, err; @@ -1628,9 +875,6 @@ void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm, snd_m pa_proplist_setf(p, "alsa.resolution_bits", "%i", bits); } - if (elem) - pa_proplist_sets(p, "alsa.mixer_element", snd_mixer_selem_get_name(elem)); - if ((err = snd_pcm_info(pcm, info)) < 0) pa_log_warn("Error fetching PCM info: %s", pa_alsa_strerror(err)); else @@ -1658,10 +902,10 @@ void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name) { return; } - if ((t = snd_ctl_card_info_get_mixername(info))) + if ((t = snd_ctl_card_info_get_mixername(info)) && *t) pa_proplist_sets(p, "alsa.mixer_name", t); - if ((t = snd_ctl_card_info_get_components(info))) + if ((t = snd_ctl_card_info_get_components(info)) && *t) pa_proplist_sets(p, "alsa.components", t); snd_ctl_close(ctl); diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index 27f43712..4993f454 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -30,95 +30,86 @@ #include #include #include +#include +#include #include #include #include -typedef struct pa_alsa_fdlist pa_alsa_fdlist; - -struct pa_alsa_fdlist *pa_alsa_fdlist_new(void); -void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl); -int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m); +#include "alsa-mixer.h" int pa_alsa_set_hw_params( snd_pcm_t *pcm_handle, - pa_sample_spec *ss, - uint32_t *periods, - snd_pcm_uframes_t *period_size, + pa_sample_spec *ss, /* modified at return */ + uint32_t *periods, /* modified at return */ + snd_pcm_uframes_t *period_size, /* modified at return */ snd_pcm_uframes_t tsched_size, - pa_bool_t *use_mmap, - pa_bool_t *use_tsched, + pa_bool_t *use_mmap, /* modified at return */ + pa_bool_t *use_tsched, /* modified at return */ pa_bool_t require_exact_channel_number); -int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min); - -typedef struct pa_alsa_profile_info { - pa_channel_map map; - const char *alsa_name; - const char *alsa_name_fallback; - const char *description; /* internationalized */ - const char *name; - unsigned priority; - const char *playback_control_name, *playback_control_fallback; - const char *record_control_name, *record_control_fallback; -} pa_alsa_profile_info; - -int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev); -snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback); -int pa_alsa_find_mixer_and_elem(snd_pcm_t *pcm, char **ctl_device, snd_mixer_t **_m, snd_mixer_elem_t **_e, const char *control_name, const pa_alsa_profile_info*profile); +int pa_alsa_set_sw_params( + snd_pcm_t *pcm, + snd_pcm_uframes_t avail_min); -void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name); - -/* Picks a working profile based on the specified ss/map */ +/* Picks a working mapping from the profile set based on the specified ss/map */ snd_pcm_t *pa_alsa_open_by_device_id_auto( const char *dev_id, - char **dev, - pa_sample_spec *ss, - pa_channel_map* map, + char **dev, /* modified at return */ + pa_sample_spec *ss, /* modified at return */ + pa_channel_map* map, /* modified at return */ int mode, - uint32_t *nfrags, - snd_pcm_uframes_t *period_size, + uint32_t *nfrags, /* modified at return */ + snd_pcm_uframes_t *period_size, /* modified at return */ snd_pcm_uframes_t tsched_size, - pa_bool_t *use_mmap, - pa_bool_t *use_tsched, - const pa_alsa_profile_info **profile); + pa_bool_t *use_mmap, /* modified at return */ + pa_bool_t *use_tsched, /* modified at return */ + pa_alsa_profile_set *ps, + pa_alsa_mapping **mapping); /* modified at return */ -/* Uses the specified profile */ -snd_pcm_t *pa_alsa_open_by_device_id_profile( +/* Uses the specified mapping */ +snd_pcm_t *pa_alsa_open_by_device_id_mapping( const char *dev_id, - char **dev, - pa_sample_spec *ss, - pa_channel_map* map, + char **dev, /* modified at return */ + pa_sample_spec *ss, /* modified at return */ + pa_channel_map* map, /* modified at return */ int mode, - uint32_t *nfrags, - snd_pcm_uframes_t *period_size, + uint32_t *nfrags, /* modified at return */ + snd_pcm_uframes_t *period_size, /* modified at return */ snd_pcm_uframes_t tsched_size, - pa_bool_t *use_mmap, - pa_bool_t *use_tsched, - const pa_alsa_profile_info *profile); + pa_bool_t *use_mmap, /* modified at return */ + pa_bool_t *use_tsched, /* modified at return */ + pa_alsa_mapping *mapping); /* Opens the explicit ALSA device */ snd_pcm_t *pa_alsa_open_by_device_string( const char *device, - char **dev, - pa_sample_spec *ss, - pa_channel_map* map, + char **dev, /* modified at return */ + pa_sample_spec *ss, /* modified at return */ + pa_channel_map* map, /* modified at return */ int mode, - uint32_t *nfrags, - snd_pcm_uframes_t *period_size, + uint32_t *nfrags, /* modified at return */ + snd_pcm_uframes_t *period_size, /* modified at return */ snd_pcm_uframes_t tsched_size, - pa_bool_t *use_mmap, - pa_bool_t *use_tsched, + pa_bool_t *use_mmap, /* modified at return */ + pa_bool_t *use_tsched, /* modified at return */ pa_bool_t require_exact_channel_number); -int pa_alsa_probe_profiles( +/* Opens the explicit ALSA device with a fallback list */ +snd_pcm_t *pa_alsa_open_by_device_string_strv( + char **device, const char *dev_id, - const pa_sample_spec *ss, - void (*cb)(const pa_alsa_profile_info *sink, const pa_alsa_profile_info *source, void *userdata), - void *userdata); - -int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback); + char **dev, /* modified at return */ + pa_sample_spec *ss, /* modified at return */ + pa_channel_map* map, /* modified at return */ + int mode, + uint32_t *nfrags, /* modified at return */ + snd_pcm_uframes_t *period_size, /* modified at return */ + snd_pcm_uframes_t tsched_size, + pa_bool_t *use_mmap, /* modified at return */ + pa_bool_t *use_tsched, /* modified at return */ + pa_bool_t require_exact_channel_number); void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm); void pa_alsa_dump_status(snd_pcm_t *pcm); @@ -128,7 +119,8 @@ void pa_alsa_redirect_errors_dec(void); void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info); void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card); -void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm, snd_mixer_elem_t *elem); +void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm); +void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name); pa_bool_t pa_alsa_init_description(pa_proplist *p); int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents); @@ -140,13 +132,11 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_si int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss); char *pa_alsa_get_driver_name(int card); - char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm); char *pa_alsa_get_reserve_name(const char *device); pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm); - pa_bool_t pa_alsa_pcm_is_modem(snd_pcm_t *pcm); const char* pa_alsa_strerror(int errnum); diff --git a/src/modules/alsa/mixer/paths/analog-input-aux.conf b/src/modules/alsa/mixer/paths/analog-input-aux.conf new file mode 100644 index 00000000..8f480567 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-aux.conf @@ -0,0 +1,32 @@ +# For devices, where we have an Aux element + +[General] +priority = 90 +name = analog-input + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Mic] +switch = off +volume = off + +[Element Line] +switch = off +volume = off + +[Element Aux] +required = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Video] +switch = off +volume = off + +.include analog-input.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-fm.conf b/src/modules/alsa/mixer/paths/analog-input-fm.conf new file mode 100644 index 00000000..0f78f39f --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-fm.conf @@ -0,0 +1,44 @@ +# For devices where we have an FM element + +[General] +priority = 70 +name = analog-input-radio + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Mic] +switch = off +volume = off + +[Element Line] +switch = off +volume = off + +[Element Aux] +switch = off +volume = off + +[Element Video] +switch = off +volume = off + +[Element Mic/Line] +switch = off +volume = off + +[Element TV Tuner] +switch = off +volume = off + +[Element FM] +required = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +.include analog-input.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-linein.conf b/src/modules/alsa/mixer/paths/analog-input-linein.conf new file mode 100644 index 00000000..b6ba738c --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-linein.conf @@ -0,0 +1,43 @@ +# For devices, where we have a Line element + +[General] +priority = 90 + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Mic] +switch = off +volume = off + +[Element Line] +required = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Aux] +switch = off +volume = off + +[Element Video] +switch = off +volume = off + +[Element Mic/Line] +switch = off +volume = off + +[Element TV Tuner] +switch = off +volume = off + +[Element FM] +switch = off +volume = off + +.include analog-input.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-mic-line.conf b/src/modules/alsa/mixer/paths/analog-input-mic-line.conf new file mode 100644 index 00000000..7d4addf7 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-mic-line.conf @@ -0,0 +1,44 @@ +# For devices where we have a Mic/Lineb element + +[General] +priority = 90 +name = analog-input + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Mic] +switch = off +volume = off + +[Element Line] +switch = off +volume = off + +[Element Aux] +switch = off +volume = off + +[Element Video] +switch = off +volume = off + +[Element Mic/Line] +required = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element TV Tuner] +switch = off +volume = off + +[Element FM] +switch = off +volume = off + +.include analog-input.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf b/src/modules/alsa/mixer/paths/analog-input-mic.conf new file mode 100644 index 00000000..004cd24a --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf @@ -0,0 +1,45 @@ +# For devices where we have a Mic element + +[General] +priority = 100 +name = analog-input-microphone + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Mic] +required = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Line] +switch = off +volume = off + +[Element Aux] +switch = off +volume = off + +[Element Video] +switch = off +volume = off + +[Element Mic/Line] +switch = off +volume = off + +[Element TV Tuner] +switch = off +volume = off + +[Element FM] +switch = off +volume = off + +.include analog-input.conf.common +.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf.common b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common new file mode 100644 index 00000000..d67ee4e3 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common @@ -0,0 +1,41 @@ +;;; 'Mic Select' + +[Element Mic Select] +enumeration = select + +[Option Mic Select:Mic1] +name = input-microphone +priority = 20 + +[Option Mic Select:Mic2] +name = input-microphone +priority = 19 + +;;; Various Boosts + +[Element Mic Boost (+20dB)] +switch = select + +[Option Mic Boost (+20dB):on] +name = input-boost-on + +[Option Mic Boost (+20dB):off] +name = input-boost-off + +[Element Mic Boost] +switch = select + +[Option Mic Boost:on] +name = input-boost-on + +[Option Mic Boost:off] +name = input-boost-off + +[Element Front Mic Boost] +switch = select + +[Option Front Mic Boost:on] +name = input-boost-on + +[Option Front Mic Boost:off] +name = input-boost-off diff --git a/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf b/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf new file mode 100644 index 00000000..ea0a0b72 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf @@ -0,0 +1,44 @@ +# For devices, where we have a TV Tuner element + +[General] +priority = 70 +name = analog-input-video + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Mic] +switch = off +volume = off + +[Element Line] +switch = off +volume = off + +[Element Aux] +switch = off +volume = off + +[Element Video] +switch = off +volume = off + +[Element Mic/Line] +switch = off +volume = off + +[Element TV Tuner] +required = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element FM] +switch = off +volume = off + +.include analog-input.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-video.conf b/src/modules/alsa/mixer/paths/analog-input-video.conf new file mode 100644 index 00000000..27acc254 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-video.conf @@ -0,0 +1,31 @@ +# For devices, where we have a Video element + +[General] +priority = 70 + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Mic] +switch = off +volume = off + +[Element Line] +switch = off +volume = off + +[Element Aux] +switch = off +volume = off + +[Element Video] +required = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +.include analog-input.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input.conf b/src/modules/alsa/mixer/paths/analog-input.conf new file mode 100644 index 00000000..b221bb44 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input.conf @@ -0,0 +1,35 @@ +# A fallback for devices that lack seperate Mic/Line/Aux/Video elements + +[General] +priority = 100 + +[Element Capture] +required = volume +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Mic] +required-absent = any + +[Element Line] +required-absent = any + +[Element Aux] +required-absent = any + +[Element Video] +required-absent = any + +[Element Mic/Line] +required-absent = any + +[Element TV Tuner] +required-absent = any + +[Element FM] +required-absent = any + +.include analog-input.conf.common +.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input.conf.common b/src/modules/alsa/mixer/paths/analog-input.conf.common new file mode 100644 index 00000000..d34afd04 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input.conf.common @@ -0,0 +1,239 @@ +# Mixer path for PulseAudio's ALSA backend. If multiple options by the +# same id are discovered they will be suffixed with a number to +# distuingish them, in the same order they appear here. +# +# Source selection should use the following names: +# +# input -- If we don't know the exact kind of input +# input-microphone +# input-microphone-internal +# input-microphone-external +# input-linein +# input-video +# input-radio +# input-docking-microphone +# input-docking-linein +# input-docking +# +# We explicitly don't want to wrap the following sources: +# +# CD +# Synth/MIDI +# Phone +# Mix +# Digital/SPDIF +# Master +# PC Speaker +# + +;;; 'Input Source Select' + +[Element Input Source Select] +enumeration = select + +[Option Input Source Select:Input1] +name = input +priority = 10 + +[Option Input Source Select:Input2] +name = input +priority = 5 + +;;; 'Input Source' + +[Element Input Source] +enumeration = select + +[Option Input Source:Mic] +name = input-microphone +priority = 20 + +[Option Input Source:Microphone] +name = input-microphone +priority = 20 + +[Option Input Source:Front Mic] +name = input-microphone +priority = 19 + +[Option Input Source:Front Microphone] +name = input-microphone +priority = 19 + +[Option Input Source:Line] +name = input-linein +priority = 18 + +[Option Input Source:Line-In] +name = input-linein +priority = 18 + +[Option Input Source:Line In] +name = input-linein +priority = 18 + +;;; ' Capture Source' + +[Element Capture Source] +enumeration = select + +[Option Capture Source:TV Tuner] +name = input-video + +[Option Capture Source:FM] +name = input-radio + +[Option Capture Source:Mic/Line] +name = input + +[Option Capture Source:Line/Mic] +name = input + +[Option Capture Source:Mic] +name = input-microphone + +[Option Capture Source:Microphone] +name = input-microphone + +[Option Capture Source:Int Mic] +name = input-microphone-internal + +[Option Capture Source:Int DMic] +name = input-microphone-internal + +[Option Capture Source:Internal Mic] +name = input-microphone-internal + +[Option Capture Source:iMic] +name = input-microphone-internal + +[Option Capture Source:i-Mic] +name = input-microphone-internal + +[Option Capture Source:Internal Microphone] +name = input-microphone-internal + +[Option Capture Source:Front Mic] +name = input-microphone + +[Option Capture Source:Front Microphone] +name = input-microphone + +[Option Capture Source:Rear Mic] +name = input-microphone + +[Option Capture Source:Mic1] +name = input-microphone + +[Option Capture Source:Mic2] +name = input-microphone + +[Option Capture Source:D-Mic] +name = input-microphone + +[Option Capture Source:IntMic] +name = input-microphone-internal + +[Option Capture Source:ExtMic] +name = input-microphone-external + +[Option Capture Source:Ext Mic] +name = input-microphone-external + +[Option Capture Source:E-Mic] +name = input-microphone-external + +[Option Capture Source:e-Mic] +name = input-microphone-external + +[Option Capture Source:LineIn] +name = input-linein + +[Option Capture Source:Analog] +name = input + +[Option Capture Source:Line] +name = input-linein + +[Option Capture Source:Line-In] +name = input-linein + +[Option Capture Source:Line In] +name = input-linein + +[Option Capture Source:Video] +name = input-video + +[Option Capture Source:Aux] +name = input + +[Option Capture Source:Aux0] +name = input + +[Option Capture Source:Aux1] +name = input + +[Option Capture Source:Aux2] +name = input + +[Option Capture Source:Aux3] +name = input + +[Option Capture Source:AUX IN] +name = input + +[Option Capture Source:Aux In] +name = input + +[Option Capture Source:AOUT] +name = input + +[Option Capture Source:AUX] +name = input + +[Option Capture Source:Cam Mic] +name = input-microphone + +[Option Capture Source:Digital Mic] +name = input-microphone + +[Option Capture Source:Digital Mic 1] +name = input-microphone + +[Option Capture Source:Digital Mic 2] +name = input-microphone + +[Option Capture Source:Analog Inputs] +name = input + +[Option Capture Source:Unknown1] +name = input + +[Option Capture Source:Unknown2] +name = input + +[Option Capture Source:Docking-Station] +name = input-docking + +[Option Capture Source:Dock Mic] +name = input-docking-microphone + +;;; Various Boosts + +[Element Capture Boost] +switch = select + +[Option Capture Boost:on] +name = input-boost-on + +[Option Capture Boost:off] +name = input-boost-off + +[Element Auto Gain Control] +switch = select + +[Option Auto Gain Control:on] +name = input-agc-on + +[Option Auto Gain Control:off] +name = input-agc-off diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf new file mode 100644 index 00000000..1a172d4c --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf @@ -0,0 +1,53 @@ +# Path for mixers that have a Headphone slider + +[General] +priority = 90 + +[Element Hardware Master] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Master] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Master Mono] +switch = off +volume = off + +[Element Headphone] +required = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Front] +switch = off +volume = off + +[Element Rear] +switch = off +volume = off + +[Element Sourround] +switch = off +volume = off + +[Element Side] +switch = off +volume = off + +[Element Center] +switch = off +volume = off + +[Element LFE] +switch = off +volume = off + +.include analog-output.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf new file mode 100644 index 00000000..67031762 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf @@ -0,0 +1,54 @@ +# Intended for usage in laptops that have a seperate LFE speaker +# connected to the Master mono connector + +[General] +priority = 40 + +[Element Hardware Master] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Master] +switch = mute +volume = merge +override-map.1 = all-no-lfe +override-map.2 = all-left,all-right + +[Element Master Mono] +required = any +switch = mute +volume = merge +override-map.1 = lfe +override-map.2 = lfe,lfe + +[Element Headphone] +switch = off +volume = off + +[Element Front] +switch = off +volume = off + +[Element Rear] +switch = off +volume = off + +[Element Sourround] +switch = off +volume = off + +[Element Side] +switch = off +volume = off + +[Element Center] +switch = off +volume = off + +[Element LFE] +switch = off +volume = off + +.include analog-output.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-output-mono.conf b/src/modules/alsa/mixer/paths/analog-output-mono.conf new file mode 100644 index 00000000..a23d9b79 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-output-mono.conf @@ -0,0 +1,51 @@ +# Intended for usage on boards that have a seperate Mono output plug. + +[General] +priority = 50 + +[Element Hardware Master] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Master] +switch = off +volume = off + +[Element Master Mono] +required = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Headphone] +switch = off +volume = off + +[Element Front] +switch = off +volume = off + +[Element Rear] +switch = off +volume = off + +[Element Sourround] +switch = off +volume = off + +[Element Side] +switch = off +volume = off + +[Element Center] +switch = off +volume = off + +[Element LFE] +switch = off +volume = off + +.include analog-output.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-output.conf b/src/modules/alsa/mixer/paths/analog-output.conf new file mode 100644 index 00000000..15e703c4 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-output.conf @@ -0,0 +1,62 @@ +# Intended for the 'default' output + +[General] +priority = 100 + +[Element Hardware Master] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Master] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Master Mono] +switch = off +volume = off + +[Element Headphone] +switch = off +volume = off + +[Element Front] +switch = mute +volume = merge +override-map.1 = all-front +override-map.2 = front-left,front-right + +[Element Rear] +switch = mute +volume = merge +override-map.1 = all-rear +override-map.2 = rear-left,rear-right + +[Element Surround] +switch = mute +volume = merge +override-map.1 = all-rear +override-map.2 = rear-left,rear-right + +[Element Side] +switch = mute +volume = merge +override-map.1 = all-side +override-map.2 = side-left,side-right + +[Element Center] +switch = mute +volume = merge +override-map.1 = all-center +override-map.2 = all-center,all-center + +[Element LFE] +switch = mute +volume = merge +override-map.1 = lfe +override-map.2 = lfe,lfe + +.include analog-output.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common new file mode 100644 index 00000000..c38eccde --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-output.conf.common @@ -0,0 +1,40 @@ +# Common part of all paths + +# [General] +# priority = ... +# description = ... +# +# [Option ...:...] +# name = ... +# priority = ... +# +# [Element ...] +# required = ignore | switch | volume | enumeration | any +# required-absent = ignore | switch | volume +# +# switch = ignore | mute | off | on | select +# volume = ignore | merge | off | zero +# enumeration = ignore | select +# +# direction = playback | capture +# direction-try-other = no | yes +# +# override-map.1 = ... +# override-map.2 = ... + +[Element PCM] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element External Amplifier] +switch = select + +[Option External Amplifier:on] +name = output-amplifier-on +priority = 0 + +[Option External Amplifier:off] +name = output-amplifier-off +priority = 10 diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf new file mode 100644 index 00000000..bced10ad --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/default.conf @@ -0,0 +1,105 @@ +# Profile definitions for PulseAudio's ALSA backend +# +# [Mapping id] +# device-strings = ... +# channel-map = ... +# description = ... +# paths-input = ... +# paths-output = ... +# element-input = ... +# element-output = ... +# priority = ... +# direction = any | input | output +# +# [Profile id] +# input-mappings = ... +# output-mappings = ... +# description = ... +# priority = ... +# skip-probe = no | yes + +[General] +auto-profiles = yes + +[Mapping analog-mono] +device-strings = hw +channel-map = mono +paths-output = analog-output analog-output-headphones analog-output-mono analog-output-lfe-on-mono +paths-input = analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line +priority = 1 + +[Mapping analog-stereo] +device-strings = front hw +channel-map = left,right +paths-output = analog-output analog-output-headphones analog-output-mono analog-output-lfe-on-mono +paths-input = analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line +priority = 10 + +[Mapping analog-surround-40] +device-strings = surround40 +channel-map = front-left,front-right,rear-left,rear-right +paths-output = analog-output analog-output-lfe-on-mono +priority = 7 +direction = output + +[Mapping analog-surround-41] +device-strings = surround41 +channel-map = front-left,front-right,rear-left,rear-right,lfe +paths-output = analog-output analog-output-lfe-on-mono +priority = 8 +direction = output + +[Mapping analog-surround-50] +device-strings = surround50 +channel-map = front-left,front-right,rear-left,rear-right,front-center +paths-output = analog-output analog-output-lfe-on-mono +priority = 7 +direction = output + +[Mapping analog-surround-51] +device-strings = surround51 +channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe +paths-output = analog-output analog-output-lfe-on-mono +priority = 8 +direction = output + +[Mapping analog-surround-71] +device-strings = surround71 +channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right +description = Analog Surround 7.1 +paths-output = analog-output analog-output-lfe-on-mono +priority = 7 +direction = output + +[Mapping iec958-stereo] +device-strings = iec958 +channel-map = left,right +priority = 5 + +[Mapping iec958-surround-40] +device-strings = iec958 +channel-map = front-left,front-right,rear-left,rear-right +priority = 1 + +[Mapping iec958-ac3-surround-40] +device-strings = a52 +channel-map = front-left,front-right,rear-left,rear-right +priority = 2 +direction = output + +[Mapping iec958-ac3-surround-51] +device-strings = a52 +channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe +priority = 3 +direction = output + +[Mapping hdmi-stereo] +device-strings = hdmi +channel-map = left,right +priority = 4 +direction = output + +#[Profile output:analog-stereo+output:iec958-stereo+input:analog-stereo] +#description = Foobar +#output-mappings = analog-stereo iec958-stereo +#input-mappings = analog-stereo diff --git a/src/modules/alsa/mixer/samples/ATI IXP--Realtek ALC655 rev 0 b/src/modules/alsa/mixer/samples/ATI IXP--Realtek ALC655 rev 0 new file mode 100644 index 00000000..082c9a1b --- /dev/null +++ b/src/modules/alsa/mixer/samples/ATI IXP--Realtek ALC655 rev 0 @@ -0,0 +1,150 @@ +Simple mixer control 'Master',0 + Capabilities: pvolume pswitch pswitch-joined + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 29 [94%] [-3.00dB] [on] + Front Right: Playback 29 [94%] [-3.00dB] [on] +Simple mixer control 'Master Mono',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'PCM',0 + Capabilities: pvolume pswitch pswitch-joined + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 23 [74%] [0.00dB] [on] + Front Right: Playback 23 [74%] [0.00dB] [on] +Simple mixer control 'Surround',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 0 [0%] [-46.50dB] [off] + Front Right: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'Surround Jack Mode',0 + Capabilities: enum + Items: 'Shared' 'Independent' + Item0: 'Shared' +Simple mixer control 'Center',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'LFE',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'Line',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'CD',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Mic',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Mono + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-34.50dB] [off] + Front Left: Capture [on] + Front Right: Capture [on] +Simple mixer control 'Mic Boost (+20dB)',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'Mic Select',0 + Capabilities: enum + Items: 'Mic1' 'Mic2' + Item0: 'Mic1' +Simple mixer control 'Video',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Phone',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Mono + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: Playback 31 [100%] [12.00dB] [off] + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'IEC958',0 + Capabilities: pswitch pswitch-joined cswitch cswitch-joined + Playback channels: Mono + Capture channels: Mono + Mono: Playback [off] Capture [off] +Simple mixer control 'IEC958 Playback AC97-SPSA',0 + Capabilities: volume volume-joined + Playback channels: Mono + Capture channels: Mono + Limits: 0 - 3 + Mono: 0 [0%] +Simple mixer control 'IEC958 Playback Source',0 + Capabilities: enum + Items: 'PCM' 'Analog In' 'IEC958 In' + Item0: 'PCM' +Simple mixer control 'PC Speaker',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 15 + Mono: Playback 0 [0%] [-45.00dB] [on] +Simple mixer control 'Aux',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [on] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [on] Capture [off] +Simple mixer control 'Mono Output Select',0 + Capabilities: enum + Items: 'Mix' 'Mic' + Item0: 'Mix' +Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch cswitch-joined + Capture channels: Front Left - Front Right + Limits: Capture 0 - 15 + Front Left: Capture 12 [80%] [18.00dB] [on] + Front Right: Capture 12 [80%] [18.00dB] [on] +Simple mixer control 'Mix',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Mix Mono',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Channel Mode',0 + Capabilities: enum + Items: '2ch' '4ch' '6ch' + Item0: '2ch' +Simple mixer control 'Duplicate Front',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'External Amplifier',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [on] diff --git a/src/modules/alsa/mixer/samples/Brooktree Bt878--Bt87x b/src/modules/alsa/mixer/samples/Brooktree Bt878--Bt87x new file mode 100644 index 00000000..b8f61fab --- /dev/null +++ b/src/modules/alsa/mixer/samples/Brooktree Bt878--Bt87x @@ -0,0 +1,24 @@ +Simple mixer control 'FM',0 + Capabilities: cswitch cswitch-joined cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Mono + Mono: Capture [off] +Simple mixer control 'Mic/Line',0 + Capabilities: cswitch cswitch-joined cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Mono + Mono: Capture [off] +Simple mixer control 'Capture',0 + Capabilities: cvolume cvolume-joined + Capture channels: Mono + Limits: Capture 0 - 15 + Mono: Capture 13 [87%] +Simple mixer control 'Capture Boost',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [on] +Simple mixer control 'TV Tuner',0 + Capabilities: cswitch cswitch-joined cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Mono + Mono: Capture [on] diff --git a/src/modules/alsa/mixer/samples/Ensoniq AudioPCI--Cirrus Logic CS4297A rev 3 b/src/modules/alsa/mixer/samples/Ensoniq AudioPCI--Cirrus Logic CS4297A rev 3 new file mode 100644 index 00000000..a500a817 --- /dev/null +++ b/src/modules/alsa/mixer/samples/Ensoniq AudioPCI--Cirrus Logic CS4297A rev 3 @@ -0,0 +1,135 @@ +Simple mixer control 'Master',0 + Capabilities: pvolume pswitch pswitch-joined + Playback channels: Front Left - Front Right + Limits: Playback 0 - 63 + Mono: + Front Left: Playback 63 [100%] [0.00dB] [on] + Front Right: Playback 63 [100%] [0.00dB] [on] +Simple mixer control 'Master Mono',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'Headphone',0 + Capabilities: pvolume pswitch pswitch-joined + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 0 [0%] [-46.50dB] [off] + Front Right: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control '3D Control - Center',0 + Capabilities: volume volume-joined + Playback channels: Mono + Capture channels: Mono + Limits: 0 - 15 + Mono: 0 [0%] +Simple mixer control '3D Control - Depth',0 + Capabilities: volume volume-joined + Playback channels: Mono + Capture channels: Mono + Limits: 0 - 15 + Mono: 0 [0%] +Simple mixer control '3D Control - Switch',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'PCM',0 + Capabilities: pvolume pswitch pswitch-joined + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 23 [74%] [0.00dB] [on] + Front Right: Playback 23 [74%] [0.00dB] [on] +Simple mixer control 'Line',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [on] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [on] +Simple mixer control 'CD',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Mic',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Mono + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: Playback 23 [74%] [0.00dB] [on] + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Mic Boost (+20dB)',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'Mic Select',0 + Capabilities: enum + Items: 'Mic1' 'Mic2' + Item0: 'Mic1' +Simple mixer control 'Video',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Phone',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Mono + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-34.50dB] [off] + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'IEC958',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'PC Speaker',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 15 + Mono: Playback 0 [0%] [-45.00dB] [off] +Simple mixer control 'Aux',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Mono Output Select',0 + Capabilities: enum + Items: 'Mix' 'Mic' + Item0: 'Mic' +Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch cswitch-joined + Capture channels: Front Left - Front Right + Limits: Capture 0 - 15 + Front Left: Capture 15 [100%] [22.50dB] [on] + Front Right: Capture 15 [100%] [22.50dB] [on] +Simple mixer control 'Mix',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Mix Mono',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'External Amplifier',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] diff --git a/src/modules/alsa/mixer/samples/HDA ATI HDMI--ATI R6xx HDMI b/src/modules/alsa/mixer/samples/HDA ATI HDMI--ATI R6xx HDMI new file mode 100644 index 00000000..244f24a8 --- /dev/null +++ b/src/modules/alsa/mixer/samples/HDA ATI HDMI--ATI R6xx HDMI @@ -0,0 +1,4 @@ +Simple mixer control 'IEC958',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [on] diff --git a/src/modules/alsa/mixer/samples/HDA Intel--Analog Devices AD1981 b/src/modules/alsa/mixer/samples/HDA Intel--Analog Devices AD1981 new file mode 100644 index 00000000..165522fa --- /dev/null +++ b/src/modules/alsa/mixer/samples/HDA Intel--Analog Devices AD1981 @@ -0,0 +1,62 @@ +Simple mixer control 'Master',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 63 + Mono: + Front Left: Playback 63 [100%] [3.00dB] [on] + Front Right: Playback 63 [100%] [3.00dB] [on] +Simple mixer control 'PCM',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 23 [74%] [0.00dB] [on] + Front Right: Playback 23 [74%] [0.00dB] [on] +Simple mixer control 'CD',0 + Capabilities: pvolume pswitch cswitch cswitch-joined cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Mono + Limits: Playback 0 - 31 + Mono: Capture [off] + Front Left: Playback 0 [0%] [-34.50dB] [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] +Simple mixer control 'Mic',0 + Capabilities: pvolume pswitch cswitch cswitch-joined cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Mono + Limits: Playback 0 - 31 + Mono: Capture [on] + Front Left: Playback 0 [0%] [-34.50dB] [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] +Simple mixer control 'Mic Boost',0 + Capabilities: volume + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: 0 - 3 + Front Left: 0 [0%] + Front Right: 0 [0%] +Simple mixer control 'IEC958',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'IEC958 Default PCM',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'IEC958 Playback Source',0 + Capabilities: enum + Items: 'PCM' 'ADC' + Item0: 'PCM' +Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch + Capture channels: Front Left - Front Right + Limits: Capture 0 - 15 + Front Left: Capture 0 [0%] [0.00dB] [on] + Front Right: Capture 0 [0%] [0.00dB] [on] +Simple mixer control 'Mix',0 + Capabilities: cswitch cswitch-joined cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Mono + Mono: Capture [off] diff --git a/src/modules/alsa/mixer/samples/HDA Intel--Realtek ALC889A b/src/modules/alsa/mixer/samples/HDA Intel--Realtek ALC889A new file mode 100644 index 00000000..28a2e73c --- /dev/null +++ b/src/modules/alsa/mixer/samples/HDA Intel--Realtek ALC889A @@ -0,0 +1,113 @@ +Simple mixer control 'Master',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 64 + Mono: Playback 64 [100%] [0.00dB] [on] +Simple mixer control 'Headphone',0 + Capabilities: pswitch + Playback channels: Front Left - Front Right + Mono: + Front Left: Playback [on] + Front Right: Playback [on] +Simple mixer control 'PCM',0 + Capabilities: pvolume + Playback channels: Front Left - Front Right + Limits: Playback 0 - 255 + Mono: + Front Left: Playback 255 [100%] [0.00dB] + Front Right: Playback 255 [100%] [0.00dB] +Simple mixer control 'Front',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 64 + Mono: + Front Left: Playback 44 [69%] [-20.00dB] [on] + Front Right: Playback 44 [69%] [-20.00dB] [on] +Simple mixer control 'Front Mic',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 0 [0%] [-34.50dB] [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] +Simple mixer control 'Front Mic Boost',0 + Capabilities: volume + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: 0 - 3 + Front Left: 0 [0%] + Front Right: 0 [0%] +Simple mixer control 'Surround',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 64 + Mono: + Front Left: Playback 0 [0%] [-64.00dB] [on] + Front Right: Playback 0 [0%] [-64.00dB] [on] +Simple mixer control 'Center',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 64 + Mono: Playback 0 [0%] [-64.00dB] [on] +Simple mixer control 'LFE',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 64 + Mono: Playback 0 [0%] [-64.00dB] [on] +Simple mixer control 'Side',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 64 + Mono: + Front Left: Playback 0 [0%] [-64.00dB] [on] + Front Right: Playback 0 [0%] [-64.00dB] [on] +Simple mixer control 'Line',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 0 [0%] [-34.50dB] [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] +Simple mixer control 'Mic',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 0 [0%] [-34.50dB] [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] +Simple mixer control 'Mic Boost',0 + Capabilities: volume + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: 0 - 3 + Front Left: 0 [0%] + Front Right: 0 [0%] +Simple mixer control 'IEC958',0 + Capabilities: pswitch pswitch-joined cswitch cswitch-joined + Playback channels: Mono + Capture channels: Mono + Mono: Playback [on] Capture [on] +Simple mixer control 'IEC958 Default PCM',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [on] +Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch + Capture channels: Front Left - Front Right + Limits: Capture 0 - 46 + Front Left: Capture 23 [50%] [7.00dB] [on] + Front Right: Capture 23 [50%] [7.00dB] [on] +Simple mixer control 'Capture',1 + Capabilities: cvolume cswitch + Capture channels: Front Left - Front Right + Limits: Capture 0 - 46 + Front Left: Capture 0 [0%] [-16.00dB] [off] + Front Right: Capture 0 [0%] [-16.00dB] [off] +Simple mixer control 'Input Source',0 + Capabilities: cenum + Items: 'Mic' 'Front Mic' 'Line' + Item0: 'Mic' +Simple mixer control 'Input Source',1 + Capabilities: cenum + Items: 'Mic' 'Front Mic' 'Line' + Item0: 'Mic' diff --git a/src/modules/alsa/mixer/samples/Intel 82801CA-ICH3--Analog Devices AD1881A b/src/modules/alsa/mixer/samples/Intel 82801CA-ICH3--Analog Devices AD1881A new file mode 100644 index 00000000..3ddd8af6 --- /dev/null +++ b/src/modules/alsa/mixer/samples/Intel 82801CA-ICH3--Analog Devices AD1881A @@ -0,0 +1,128 @@ +Simple mixer control 'Master',0 + Capabilities: pvolume pswitch pswitch-joined + Playback channels: Front Left - Front Right + Limits: Playback 0 - 63 + Mono: + Front Left: Playback 44 [70%] [-28.50dB] [on] + Front Right: Playback 60 [95%] [-4.50dB] [on] +Simple mixer control 'Master Mono',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 31 + Mono: Playback 17 [55%] [-21.00dB] [on] +Simple mixer control '3D Control - Center',0 + Capabilities: volume volume-joined + Playback channels: Mono + Capture channels: Mono + Limits: 0 - 15 + Mono: 0 [0%] +Simple mixer control '3D Control - Depth',0 + Capabilities: volume volume-joined + Playback channels: Mono + Capture channels: Mono + Limits: 0 - 15 + Mono: 0 [0%] +Simple mixer control '3D Control - Switch',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'PCM',0 + Capabilities: pvolume pswitch pswitch-joined + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 9 [29%] [-21.00dB] [on] + Front Right: Playback 9 [29%] [-21.00dB] [on] +Simple mixer control 'PCM Out Path & Mute',0 + Capabilities: enum + Items: 'pre 3D' 'post 3D' + Item0: 'pre 3D' +Simple mixer control 'Line',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'CD',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 9 [29%] [-21.00dB] [on] Capture [off] + Front Right: Playback 9 [29%] [-21.00dB] [on] Capture [off] +Simple mixer control 'Mic',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Mono + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-34.50dB] [off] + Front Left: Capture [on] + Front Right: Capture [on] +Simple mixer control 'Mic Boost (+20dB)',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'Mic Select',0 + Capabilities: enum + Items: 'Mic1' 'Mic2' + Item0: 'Mic1' +Simple mixer control 'Video',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Phone',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Mono + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-34.50dB] [off] + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'PC Speaker',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 15 + Mono: Playback 8 [53%] [-21.00dB] [on] +Simple mixer control 'Aux',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Mono Output Select',0 + Capabilities: enum + Items: 'Mix' 'Mic' + Item0: 'Mix' +Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch cswitch-joined + Capture channels: Front Left - Front Right + Limits: Capture 0 - 15 + Front Left: Capture 13 [87%] [19.50dB] [on] + Front Right: Capture 13 [87%] [19.50dB] [on] +Simple mixer control 'Mix',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Mix Mono',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'External Amplifier',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [on] diff --git a/src/modules/alsa/mixer/samples/Logitech USB Speaker--USB Mixer b/src/modules/alsa/mixer/samples/Logitech USB Speaker--USB Mixer new file mode 100644 index 00000000..38cf6778 --- /dev/null +++ b/src/modules/alsa/mixer/samples/Logitech USB Speaker--USB Mixer @@ -0,0 +1,27 @@ +Simple mixer control 'Bass',0 + Capabilities: volume volume-joined + Playback channels: Mono + Capture channels: Mono + Limits: 0 - 48 + Mono: 22 [46%] +Simple mixer control 'Bass Boost',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'Treble',0 + Capabilities: volume volume-joined + Playback channels: Mono + Capture channels: Mono + Limits: 0 - 48 + Mono: 25 [52%] +Simple mixer control 'PCM',0 + Capabilities: pvolume pswitch pswitch-joined + Playback channels: Front Left - Front Right + Limits: Playback 0 - 44 + Mono: + Front Left: Playback 10 [23%] [-31.00dB] [on] + Front Right: Playback 10 [23%] [-31.00dB] [on] +Simple mixer control 'Auto Gain Control',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] diff --git a/src/modules/alsa/mixer/samples/USB Audio--USB Mixer b/src/modules/alsa/mixer/samples/USB Audio--USB Mixer new file mode 100644 index 00000000..9cb4fa7f --- /dev/null +++ b/src/modules/alsa/mixer/samples/USB Audio--USB Mixer @@ -0,0 +1,37 @@ +Simple mixer control 'Master',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 255 + Mono: Playback 105 [41%] [-28.97dB] [on] +Simple mixer control 'Line',0 + Capabilities: pvolume cvolume pswitch pswitch-joined cswitch cswitch-joined + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 255 Capture 0 - 128 + Front Left: Playback 191 [75%] [34.38dB] [off] Capture 0 [0%] [0.18dB] [off] + Front Right: Playback 191 [75%] [34.38dB] [off] Capture 0 [0%] [0.18dB] [off] +Simple mixer control 'Mic',0 + Capabilities: pvolume pvolume-joined cvolume cvolume-joined pswitch pswitch-joined cswitch cswitch-joined cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Mono + Capture channels: Mono + Limits: Playback 0 - 255 Capture 0 - 128 + Mono: Playback 191 [75%] [34.38dB] [off] Capture 0 [0%] [0.18dB] [on] +Simple mixer control 'Mic Capture',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'IEC958 In',0 + Capabilities: cswitch cswitch-joined + Capture channels: Mono + Mono: Capture [off] +Simple mixer control 'Input 1',0 + Capabilities: cswitch cswitch-joined cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Mono + Mono: Capture [off] +Simple mixer control 'Input 2',0 + Capabilities: cswitch cswitch-joined cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Mono + Mono: Capture [off] diff --git a/src/modules/alsa/mixer/samples/USB Device 0x46d:0x9a4--USB Mixer b/src/modules/alsa/mixer/samples/USB Device 0x46d:0x9a4--USB Mixer new file mode 100644 index 00000000..783f826f --- /dev/null +++ b/src/modules/alsa/mixer/samples/USB Device 0x46d:0x9a4--USB Mixer @@ -0,0 +1,5 @@ +Simple mixer control 'Mic',0 + Capabilities: cvolume cvolume-joined cswitch cswitch-joined + Capture channels: Mono + Limits: Capture 0 - 3072 + Mono: Capture 1536 [50%] [23.00dB] [on] diff --git a/src/modules/alsa/mixer/samples/VIA 8237--Analog Devices AD1888 b/src/modules/alsa/mixer/samples/VIA 8237--Analog Devices AD1888 new file mode 100644 index 00000000..15e7b5a6 --- /dev/null +++ b/src/modules/alsa/mixer/samples/VIA 8237--Analog Devices AD1888 @@ -0,0 +1,211 @@ +Simple mixer control 'Master',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 31 [100%] [0.00dB] [on] + Front Right: Playback 31 [100%] [0.00dB] [on] +Simple mixer control 'Master Mono',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'Master Surround',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 0 [0%] [-46.50dB] [off] + Front Right: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'Headphone Jack Sense',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'PCM',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 23 [74%] [0.00dB] [on] + Front Right: Playback 23 [74%] [0.00dB] [on] +Simple mixer control 'Surround',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 0 [0%] [-46.50dB] [off] + Front Right: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'Surround Jack Mode',0 + Capabilities: enum + Items: 'Shared' 'Independent' + Item0: 'Shared' +Simple mixer control 'Center',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 31 + Mono: Playback 31 [100%] [0.00dB] [off] +Simple mixer control 'LFE',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'Line',0 + Capabilities: pvolume pswitch cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Line Jack Sense',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'CD',0 + Capabilities: pvolume pswitch cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Mic',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Mono + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-34.50dB] [off] + Front Left: Capture [on] + Front Right: Capture [on] +Simple mixer control 'Mic Boost (+20dB)',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'Mic Select',0 + Capabilities: enum + Items: 'Mic1' 'Mic2' + Item0: 'Mic1' +Simple mixer control 'Video',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Phone',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Mono + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-34.50dB] [off] + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'IEC958',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'IEC958 Output',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'IEC958 Playback AC97-SPSA',0 + Capabilities: volume volume-joined + Playback channels: Mono + Capture channels: Mono + Limits: 0 - 3 + Mono: 3 [100%] +Simple mixer control 'IEC958 Playback Source',0 + Capabilities: enum + Items: 'AC-Link' 'A/D Converter' + Item0: 'AC-Link' +Simple mixer control 'Aux',0 + Capabilities: pvolume pswitch cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch + Capture channels: Front Left - Front Right + Limits: Capture 0 - 15 + Front Left: Capture 0 [0%] [0.00dB] [on] + Front Right: Capture 0 [0%] [0.00dB] [on] +Simple mixer control 'Mix',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Mix Mono',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Channel Mode',0 + Capabilities: enum + Items: '2ch' '4ch' '6ch' + Item0: '2ch' +Simple mixer control 'Downmix',0 + Capabilities: enum + Items: 'Off' '6 -> 4' '6 -> 2' + Item0: 'Off' +Simple mixer control 'Exchange Front/Surround',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'External Amplifier',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [on] +Simple mixer control 'High Pass Filter Enable',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'Input Source Select',0 + Capabilities: enum + Items: 'Input1' 'Input2' + Item0: 'Input1' +Simple mixer control 'Input Source Select',1 + Capabilities: enum + Items: 'Input1' 'Input2' + Item0: 'Input1' +Simple mixer control 'Spread Front to Surround and Center/LFE',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'VIA DXS',0 + Capabilities: pvolume + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 31 [100%] [-48.00dB] + Front Right: Playback 31 [100%] [-48.00dB] +Simple mixer control 'VIA DXS',1 + Capabilities: pvolume + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 31 [100%] [-48.00dB] + Front Right: Playback 31 [100%] [-48.00dB] +Simple mixer control 'VIA DXS',2 + Capabilities: pvolume + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 31 [100%] [-48.00dB] + Front Right: Playback 31 [100%] [-48.00dB] +Simple mixer control 'VIA DXS',3 + Capabilities: pvolume + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 31 [100%] [-48.00dB] + Front Right: Playback 31 [100%] [-48.00dB] +Simple mixer control 'V_REFOUT Enable',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [on] diff --git a/src/modules/alsa/mixer/samples/VIA 8237--C-Media Electronics CMI9761A+ b/src/modules/alsa/mixer/samples/VIA 8237--C-Media Electronics CMI9761A+ new file mode 100644 index 00000000..d4f3db62 --- /dev/null +++ b/src/modules/alsa/mixer/samples/VIA 8237--C-Media Electronics CMI9761A+ @@ -0,0 +1,160 @@ +Simple mixer control 'Master',0 + Capabilities: pvolume pswitch pswitch-joined + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 0 [0%] [-46.50dB] [off] + Front Right: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'PCM',0 + Capabilities: pvolume pswitch pswitch-joined + Playback channels: Front Left - Front Right + Limits: Playback 0 - 31 + Mono: + Front Left: Playback 31 [100%] [-48.00dB] [off] + Front Right: Playback 31 [100%] [-48.00dB] [off] +Simple mixer control 'Surround',0 + Capabilities: pswitch + Playback channels: Front Left - Front Right + Mono: + Front Left: Playback [off] + Front Right: Playback [off] +Simple mixer control 'Surround Jack Mode',0 + Capabilities: enum + Items: 'Shared' 'Independent' + Item0: 'Shared' +Simple mixer control 'Center',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 31 + Mono: Playback 31 [100%] [0.00dB] [off] +Simple mixer control 'LFE',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 31 + Mono: Playback 0 [0%] [-46.50dB] [off] +Simple mixer control 'Line',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'CD',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Mic',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [on] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [on] +Simple mixer control 'Mic Boost (+20dB)',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'Mic Select',0 + Capabilities: enum + Items: 'Mic1' 'Mic2' + Item0: 'Mic1' +Simple mixer control 'Video',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Phone',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'IEC958',0 + Capabilities: pswitch pswitch-joined cswitch cswitch-joined + Playback channels: Mono + Capture channels: Mono + Mono: Playback [off] Capture [off] +Simple mixer control 'IEC958 Capture Monitor',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'IEC958 Capture Valid',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'IEC958 Output',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [off] +Simple mixer control 'IEC958 Playback AC97-SPSA',0 + Capabilities: volume volume-joined + Playback channels: Mono + Capture channels: Mono + Limits: 0 - 3 + Mono: 3 [100%] +Simple mixer control 'IEC958 Playback Source',0 + Capabilities: enum + Items: 'AC-Link' 'ADC' 'SPDIF-In' + Item0: 'AC-Link' +Simple mixer control 'PC Speaker',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 15 + Mono: Playback 0 [0%] [-45.00dB] [off] +Simple mixer control 'Aux',0 + Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive + Capture exclusive group: 0 + Playback channels: Front Left - Front Right + Capture channels: Front Left - Front Right + Limits: Playback 0 - 31 + Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off] + Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off] +Simple mixer control 'Mono Output Select',0 + Capabilities: enum + Items: 'Mix' 'Mic' + Item0: 'Mix' +Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch cswitch-joined + Capture channels: Front Left - Front Right + Limits: Capture 0 - 15 + Front Left: Capture 0 [0%] [0.00dB] [on] + Front Right: Capture 0 [0%] [0.00dB] [on] +Simple mixer control 'Mix',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Mix Mono',0 + Capabilities: cswitch cswitch-exclusive + Capture exclusive group: 0 + Capture channels: Front Left - Front Right + Front Left: Capture [off] + Front Right: Capture [off] +Simple mixer control 'Channel Mode',0 + Capabilities: enum + Items: '2ch' '4ch' '6ch' + Item0: '2ch' +Simple mixer control 'DAC Clock Source',0 + Capabilities: enum + Items: 'AC-Link' 'SPDIF-In' 'Both' + Item0: 'AC-Link' +Simple mixer control 'External Amplifier',0 + Capabilities: pswitch pswitch-joined + Playback channels: Mono + Mono: Playback [on] +Simple mixer control 'Input Source Select',0 + Capabilities: enum + Items: 'Input1' 'Input2' + Item0: 'Input1' +Simple mixer control 'Input Source Select',1 + Capabilities: enum + Items: 'Input1' 'Input2' + Item0: 'Input1' diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index ad52f5e3..e8a7f206 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -32,6 +32,10 @@ #include +#ifdef HAVE_UDEV +#include +#endif + #include "alsa-util.h" #include "alsa-sink.h" #include "alsa-source.h" @@ -92,81 +96,53 @@ struct userdata { char *device_id; pa_card *card; - pa_sink *sink; - pa_source *source; pa_modargs *modargs; - pa_hashmap *profiles; + pa_alsa_profile_set *profile_set; }; struct profile_data { - const pa_alsa_profile_info *sink_profile, *source_profile; + pa_alsa_profile *profile; }; -static void enumerate_cb( - const pa_alsa_profile_info *sink, - const pa_alsa_profile_info *source, - void *userdata) { +static void add_profiles(struct userdata *u, pa_hashmap *h) { + pa_alsa_profile *ap; + void *state; - struct userdata *u = userdata; - char *t, *n; - pa_card_profile *p; - struct profile_data *d; - unsigned bonus = 0; - - if (sink && source) { - n = pa_sprintf_malloc("output-%s+input-%s", sink->name, source->name); - t = pa_sprintf_malloc(_("Output %s + Input %s"), sink->description, _(source->description)); - } else if (sink) { - n = pa_sprintf_malloc("output-%s", sink->name); - t = pa_sprintf_malloc(_("Output %s"), _(sink->description)); - } else { - pa_assert(source); - n = pa_sprintf_malloc("input-%s", source->name); - t = pa_sprintf_malloc(_("Input %s"), _(source->description)); - } - - if (sink) { - if (pa_channel_map_equal(&sink->map, &u->core->default_channel_map)) - bonus += 50000; - else if (sink->map.channels == u->core->default_channel_map.channels) - bonus += 40000; - } - - if (source) { - if (pa_channel_map_equal(&source->map, &u->core->default_channel_map)) - bonus += 30000; - else if (source->map.channels == u->core->default_channel_map.channels) - bonus += 20000; - } + pa_assert(u); + pa_assert(h); - pa_log_info("Found profile '%s'", t); + PA_HASHMAP_FOREACH(ap, u->profile_set->profiles, state) { + struct profile_data *d; + pa_card_profile *cp; + pa_alsa_mapping *m; + uint32_t idx; - p = pa_card_profile_new(n, t, sizeof(struct profile_data)); + cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data)); + cp->priority = ap->priority; - pa_xfree(t); - pa_xfree(n); + if (ap->output_mappings) { + cp->n_sinks = pa_idxset_size(ap->output_mappings); - p->priority = - (sink ? sink->priority : 0) * 100 + - (source ? source->priority : 0) + - bonus; - - p->n_sinks = !!sink; - p->n_sources = !!source; + PA_IDXSET_FOREACH(m, ap->output_mappings, idx) + if (m->channel_map.channels > cp->max_sink_channels) + cp->max_sink_channels = m->channel_map.channels; + } - if (sink) - p->max_sink_channels = sink->map.channels; - if (source) - p->max_source_channels = source->map.channels; + if (ap->input_mappings) { + cp->n_sources = pa_idxset_size(ap->input_mappings); - d = PA_CARD_PROFILE_DATA(p); + PA_IDXSET_FOREACH(m, ap->input_mappings, idx) + if (m->channel_map.channels > cp->max_source_channels) + cp->max_source_channels = m->channel_map.channels; + } - d->sink_profile = sink; - d->source_profile = source; + d = PA_CARD_PROFILE_DATA(cp); + d->profile = ap; - pa_hashmap_put(u->profiles, p->name, p); + pa_hashmap_put(h, cp->name, cp); + } } static void add_disabled_profile(pa_hashmap *profiles) { @@ -176,7 +152,7 @@ static void add_disabled_profile(pa_hashmap *profiles) { p = pa_card_profile_new("off", _("Off"), sizeof(struct profile_data)); d = PA_CARD_PROFILE_DATA(p); - d->sink_profile = d->source_profile = NULL; + d->profile = NULL; pa_hashmap_put(profiles, p->name, p); } @@ -184,6 +160,9 @@ static void add_disabled_profile(pa_hashmap *profiles) { static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { struct userdata *u; struct profile_data *nd, *od; + uint32_t idx; + pa_alsa_mapping *am; + pa_queue *sink_inputs = NULL, *source_outputs = NULL; pa_assert(c); pa_assert(new_profile); @@ -192,67 +171,85 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { nd = PA_CARD_PROFILE_DATA(new_profile); od = PA_CARD_PROFILE_DATA(c->active_profile); - if (od->sink_profile != nd->sink_profile) { - pa_queue *inputs = NULL; + if (od->profile && od->profile->output_mappings) + PA_IDXSET_FOREACH(am, od->profile->output_mappings, idx) { + if (!am->sink) + continue; - if (u->sink) { - if (nd->sink_profile) - inputs = pa_sink_move_all_start(u->sink); + if (nd->profile && + nd->profile->output_mappings && + pa_idxset_get_by_data(nd->profile->output_mappings, am, NULL)) + continue; - pa_alsa_sink_free(u->sink); - u->sink = NULL; + sink_inputs = pa_sink_move_all_start(am->sink, sink_inputs); + pa_alsa_sink_free(am->sink); + am->sink = NULL; } - if (nd->sink_profile) { - u->sink = pa_alsa_sink_new(c->module, u->modargs, __FILE__, c, nd->sink_profile); + if (od->profile && od->profile->input_mappings) + PA_IDXSET_FOREACH(am, od->profile->input_mappings, idx) { + if (!am->source) + continue; - if (inputs) { - if (u->sink) - pa_sink_move_all_finish(u->sink, inputs, FALSE); - else - pa_sink_move_all_fail(inputs); - } + if (nd->profile && + nd->profile->input_mappings && + pa_idxset_get_by_data(nd->profile->input_mappings, am, NULL)) + continue; + + source_outputs = pa_source_move_all_start(am->source, source_outputs); + pa_alsa_source_free(am->source); + am->source = NULL; } - } - if (od->source_profile != nd->source_profile) { - pa_queue *outputs = NULL; + if (nd->profile && nd->profile->output_mappings) + PA_IDXSET_FOREACH(am, nd->profile->output_mappings, idx) { - if (u->source) { - if (nd->source_profile) - outputs = pa_source_move_all_start(u->source); + if (!am->sink) + am->sink = pa_alsa_sink_new(c->module, u->modargs, __FILE__, c, am); - pa_alsa_source_free(u->source); - u->source = NULL; + if (sink_inputs && am->sink) { + pa_sink_move_all_finish(am->sink, sink_inputs, FALSE); + sink_inputs = NULL; + } } - if (nd->source_profile) { - u->source = pa_alsa_source_new(c->module, u->modargs, __FILE__, c, nd->source_profile); + if (nd->profile && nd->profile->input_mappings) + PA_IDXSET_FOREACH(am, nd->profile->input_mappings, idx) { - if (outputs) { - if (u->source) - pa_source_move_all_finish(u->source, outputs, FALSE); - else - pa_source_move_all_fail(outputs); + if (!am->source) + am->source = pa_alsa_source_new(c->module, u->modargs, __FILE__, c, am); + + if (source_outputs && am->source) { + pa_source_move_all_finish(am->source, source_outputs, FALSE); + source_outputs = NULL; } } - } + + if (sink_inputs) + pa_sink_move_all_fail(sink_inputs); + + if (source_outputs) + pa_source_move_all_fail(source_outputs); return 0; } static void init_profile(struct userdata *u) { + uint32_t idx; + pa_alsa_mapping *am; struct profile_data *d; pa_assert(u); d = PA_CARD_PROFILE_DATA(u->card->active_profile); - if (d->sink_profile) - u->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, d->sink_profile); + if (d->profile && d->profile->output_mappings) + PA_IDXSET_FOREACH(am, d->profile->output_mappings, idx) + am->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, am); - if (d->source_profile) - u->source = pa_alsa_source_new(u->module, u->modargs, __FILE__, u->card, d->source_profile); + if (d->profile && d->profile->input_mappings) + PA_IDXSET_FOREACH(am, d->profile->input_mappings, idx) + am->source = pa_alsa_source_new(u->module, u->modargs, __FILE__, u->card, am); } static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *device_id) { @@ -286,9 +283,9 @@ int pa__init(pa_module *m) { pa_modargs *ma; int alsa_card_index; struct userdata *u; - char rname[32]; pa_reserve_wrapper *reserve = NULL; const char *description; + char *fn = NULL; pa_alsa_redirect_errors_inc(); snd_config_update_free_global(); @@ -300,13 +297,10 @@ int pa__init(pa_module *m) { goto fail; } - m->userdata = u = pa_xnew(struct userdata, 1); + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; u->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID)); - u->card = NULL; - u->sink = NULL; - u->source = NULL; u->modargs = ma; if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) { @@ -314,16 +308,36 @@ int pa__init(pa_module *m) { goto fail; } - pa_snprintf(rname, sizeof(rname), "Audio%i", alsa_card_index); + if (!pa_in_system_mode()) { + char *rname; + + if ((rname = pa_alsa_get_reserve_name(u->device_id))) { + reserve = pa_reserve_wrapper_get(m->core, rname); + pa_xfree(rname); + + if (!reserve) + goto fail; + } + } + +#ifdef HAVE_UDEV + fn = pa_udev_get_property(alsa_card_index, "PULSE_PROFILE_SET"); +#endif + + u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map); + pa_xfree(fn); + + if (!u->profile_set) + goto fail; - if (!pa_in_system_mode()) - if (!(reserve = pa_reserve_wrapper_get(m->core, rname))) - goto fail; + pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec); pa_card_new_data_init(&data); data.driver = __FILE__; data.module = m; + pa_alsa_init_proplist_card(m->core, data.proplist, alsa_card_index); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id); pa_alsa_init_description(data.proplist); set_card_name(&data, ma, u->device_id); @@ -332,11 +346,8 @@ int pa__init(pa_module *m) { if ((description = pa_proplist_gets(data.proplist, PA_PROP_DEVICE_DESCRIPTION))) pa_reserve_wrapper_set_application_device_name(reserve, description); - u->profiles = data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - if (pa_alsa_probe_profiles(u->device_id, &m->core->default_sample_spec, enumerate_cb, u) < 0) { - pa_card_new_data_done(&data); - goto fail; - } + data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + add_profiles(u, data.profiles); if (pa_hashmap_isempty(data.profiles)) { pa_log("Failed to find a working profile."); @@ -379,13 +390,22 @@ fail: int pa__get_n_used(pa_module *m) { struct userdata *u; + int n = 0; + uint32_t idx; + pa_sink *sink; + pa_source *source; pa_assert(m); pa_assert_se(u = m->userdata); + pa_assert(u->card); + + PA_IDXSET_FOREACH(sink, u->card->sinks, idx) + n += pa_sink_linked_by(sink); - return - (u->sink ? pa_sink_linked_by(u->sink) : 0) + - (u->source ? pa_source_linked_by(u->source) : 0); + PA_IDXSET_FOREACH(source, u->card->sources, idx) + n += pa_source_linked_by(source); + + return n; } void pa__done(pa_module*m) { @@ -396,11 +416,19 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) goto finish; - if (u->sink) - pa_alsa_sink_free(u->sink); + if (u->card && u->card->sinks) { + pa_sink *s; + + while ((s = pa_idxset_steal_first(u->card->sinks, NULL))) + pa_alsa_sink_free(s); + } + + if (u->card && u->card->sources) { + pa_source *s; - if (u->source) - pa_alsa_source_free(u->source); + while ((s = pa_idxset_steal_first(u->card->sources, NULL))) + pa_alsa_source_free(s); + } if (u->card) pa_card_free(u->card); @@ -408,6 +436,9 @@ void pa__done(pa_module*m) { if (u->modargs) pa_modargs_free(u->modargs); + if (u->profile_set) + pa_alsa_profile_set_free(u->profile_set); + pa_xfree(u->device_id); pa_xfree(u); diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 40093cf2..9fec4edf 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1443,12 +1443,12 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us if (u->sink && dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged")) { pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); - pa_sink_volume_changed(u->sink, &v); + pa_sink_volume_changed(u->sink, &v, TRUE); } else if (u->source && dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) { pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); - pa_source_volume_changed(u->source, &v); + pa_source_volume_changed(u->source, &v, TRUE); } } } @@ -1938,7 +1938,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { } if (u->sink) { - inputs = pa_sink_move_all_start(u->sink); + inputs = pa_sink_move_all_start(u->sink, NULL); #ifdef NOKIA if (!USE_SCO_OVER_PCM(u)) #endif @@ -1946,7 +1946,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { } if (u->source) { - outputs = pa_source_move_all_start(u->source); + outputs = pa_source_move_all_start(u->source, NULL); #ifdef NOKIA if (!USE_SCO_OVER_PCM(u)) #endif diff --git a/src/modules/bluetooth/module-bluetooth-proximity.c b/src/modules/bluetooth/module-bluetooth-proximity.c index 9993c8dc..c4cfd733 100644 --- a/src/modules/bluetooth/module-bluetooth-proximity.c +++ b/src/modules/bluetooth/module-bluetooth-proximity.c @@ -109,7 +109,7 @@ static void update_volume(struct userdata *u) { } pa_log_info("Found %u BT devices, unmuting.", u->n_found); - pa_sink_set_mute(s, FALSE); + pa_sink_set_mute(s, FALSE, FALSE); } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) { pa_sink *s; @@ -122,7 +122,7 @@ static void update_volume(struct userdata *u) { } pa_log_info("No BT devices found, muting."); - pa_sink_set_mute(s, TRUE); + pa_sink_set_mute(s, TRUE, FALSE); } else pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown); diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 15af74a6..21f4a8f1 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -27,6 +27,7 @@ #endif #include +#include #include #include @@ -45,20 +46,20 @@ #include "ladspa.h" PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("Virtual LADSPA sink"); +PA_MODULE_DESCRIPTION(_("Virtual LADSPA sink")); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( - "sink_name= " - "sink_properties= " - "master= " - "format= " - "rate= " - "channels= " - "channel_map= " - "plugin= " - "label= " - "control="); + _("sink_name= " + "sink_properties= " + "master= " + "format= " + "rate= " + "channels= " + "channel_map= " + "plugin= " + "label= " + "control=")); #define MEMBLOCKQ_MAXLENGTH (16*1024*1024) diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c index a1a8726f..06efeb8f 100644 --- a/src/modules/module-lirc.c +++ b/src/modules/module-lirc.c @@ -112,7 +112,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event volchange = RESET; if (volchange == INVALID) - pa_log_warn("Recieved unknown IR code '%s'", name); + pa_log_warn("Received unknown IR code '%s'", name); else { pa_sink *s; @@ -133,7 +133,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event cv.values[i] = PA_VOLUME_MAX; } - pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE); + pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE); break; case DOWN: @@ -144,20 +144,20 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event cv.values[i] = PA_VOLUME_MUTED; } - pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE); + pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE); break; case MUTE: - pa_sink_set_mute(s, TRUE); + pa_sink_set_mute(s, TRUE, TRUE); break; case RESET: - pa_sink_set_mute(s, FALSE); + pa_sink_set_mute(s, FALSE, TRUE); break; case MUTE_TOGGLE: - pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE)); + pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE), TRUE); break; case INVALID: diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c index d8b9c79e..b30fae51 100644 --- a/src/modules/module-mmkbd-evdev.c +++ b/src/modules/module-mmkbd-evdev.c @@ -115,7 +115,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event cv.values[i] = PA_VOLUME_MAX; } - pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE); + pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE); break; case DOWN: @@ -126,12 +126,12 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event cv.values[i] = PA_VOLUME_MUTED; } - pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE); + pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE); break; case MUTE_TOGGLE: - pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE)); + pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE), TRUE); break; case INVALID: diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 6f525da3..c493d9bb 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -730,7 +730,7 @@ static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, p } if (channel != u->channel) { - pa_log("Recieved data for invalid channel"); + pa_log("Received data for invalid channel"); goto fail; } @@ -1157,10 +1157,10 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag pa_cvolume_equal(&volume, &u->sink->virtual_volume)) return; - pa_sink_volume_changed(u->sink, &volume); + pa_sink_volume_changed(u->sink, &volume, FALSE); if (u->version >= 11) - pa_sink_mute_changed(u->sink, mute); + pa_sink_mute_changed(u->sink, mute, FALSE); return; @@ -1675,7 +1675,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o pa_assert(u); if (channel != u->channel) { - pa_log("Recieved memory block on bad channel."); + pa_log("Received memory block on bad channel."); pa_module_unload_request(u->module, TRUE); return; } diff --git a/src/modules/udev-util.c b/src/modules/udev-util.c index de8f5f2f..cc824465 100644 --- a/src/modules/udev-util.c +++ b/src/modules/udev-util.c @@ -58,7 +58,7 @@ static int read_id(struct udev_device *d, const char *n) { return u; } -int pa_udev_get_info(pa_core *core, pa_proplist *p, int card_idx) { +int pa_udev_get_info(int card_idx, pa_proplist *p) { int r = -1; struct udev *udev; struct udev_device *card = NULL; @@ -66,7 +66,6 @@ int pa_udev_get_info(pa_core *core, pa_proplist *p, int card_idx) { const char *v; int id; - pa_assert(core); pa_assert(p); pa_assert(card_idx >= 0); @@ -153,3 +152,40 @@ finish: return r; } + +char* pa_udev_get_property(int card_idx, const char *name) { + struct udev *udev; + struct udev_device *card = NULL; + char *t, *r = NULL; + const char *v; + + pa_assert(card_idx >= 0); + pa_assert(name); + + if (!(udev = udev_new())) { + pa_log_error("Failed to allocate udev context."); + goto finish; + } + + t = pa_sprintf_malloc("%s/class/sound/card%i", udev_get_sys_path(udev), card_idx); + card = udev_device_new_from_syspath(udev, t); + pa_xfree(t); + + if (!card) { + pa_log_error("Failed to get card object."); + goto finish; + } + + if ((v = udev_device_get_property_value(card, name)) && *v) + r = pa_xstrdup(v); + +finish: + + if (card) + udev_device_unref(card); + + if (udev) + udev_unref(udev); + + return r; +} diff --git a/src/modules/udev-util.h b/src/modules/udev-util.h index 5120abdd..8523bc4d 100644 --- a/src/modules/udev-util.h +++ b/src/modules/udev-util.h @@ -25,6 +25,7 @@ #include -int pa_udev_get_info(pa_core *core, pa_proplist *p, int card); +int pa_udev_get_info(int card_idx, pa_proplist *p); +char* pa_udev_get_property(int card_idx, const char *name); #endif diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 644de96e..e2c3c066 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -125,6 +125,8 @@ static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); /* A method table for all available commands */ @@ -176,10 +178,12 @@ static const struct command commands[] = { { "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3}, { "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2}, { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (args: index, name)", 3}, + { "set-sink-port", pa_cli_command_sink_port, "Change the port of a sink (args: index, name)", 3}, + { "set-source-port", pa_cli_command_source_port, "Change the port of a source (args: index, name)", 3}, { "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2}, { "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2}, { "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2}, - { "set-log-backtrace", pa_cli_command_log_backtrace, "Show bakctrace in log messages (args: frames)", 2}, + { "set-log-backtrace", pa_cli_command_log_backtrace, "Show backtrace in log messages (args: frames)", 2}, { NULL, NULL, NULL, 0 } }; @@ -526,7 +530,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu } pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume); - pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE); + pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE, TRUE); return 0; } @@ -604,7 +608,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf * } pa_cvolume_set(&cvolume, source->sample_spec.channels, volume); - pa_source_set_volume(source, &cvolume); + pa_source_set_volume(source, &cvolume, TRUE); return 0; } @@ -638,7 +642,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, return -1; } - pa_sink_set_mute(sink, mute); + pa_sink_set_mute(sink, mute, TRUE); return 0; } @@ -672,7 +676,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu return -1; } - pa_source_set_mute(source, mute); + pa_source_set_mute(source, mute, TRUE); return 0; } @@ -1476,6 +1480,70 @@ static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *b return 0; } +static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *n, *p; + pa_sink *sink; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); + return -1; + } + + if (!(p = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a profile by its name.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) { + pa_strbuf_puts(buf, "No sink found by this name or index.\n"); + return -1; + } + + if (pa_sink_set_port(sink, p, TRUE) < 0) { + pa_strbuf_printf(buf, "Failed to set sink port to '%s'.\n", p); + return -1; + } + + return 0; +} + +static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *n, *p; + pa_source *source; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); + return -1; + } + + if (!(p = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a profile by its name.\n"); + return -1; + } + + if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) { + pa_strbuf_puts(buf, "No source found by this name or index.\n"); + return -1; + } + + if (pa_source_set_port(source, p, TRUE) < 0) { + pa_strbuf_printf(buf, "Failed to set source port to '%s'.\n", p); + return -1; + } + + return 0; +} + static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { pa_module *m; pa_sink *sink; diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index bc863f05..9395513d 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -139,11 +139,10 @@ char *pa_card_list_to_string(pa_core *c) { if (card->profiles) { pa_card_profile *p; - void *state = NULL; + void *state; pa_strbuf_puts(s, "\tprofiles:\n"); - - while ((p = pa_hashmap_iterate(card->profiles, &state, NULL))) + PA_HASHMAP_FOREACH(p, card->profiles, state) pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority); } @@ -307,6 +306,22 @@ char *pa_sink_list_to_string(pa_core *c) { t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t"); pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); + + if (sink->ports) { + pa_device_port *p; + void *state; + + pa_strbuf_puts(s, "\tports:\n"); + PA_HASHMAP_FOREACH(p, sink->ports, state) + pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority); + } + + + if (sink->active_port) + pa_strbuf_printf( + s, + "\tactive port: <%s>\n", + sink->active_port->name); } return pa_strbuf_tostring_free(s); @@ -412,6 +427,21 @@ char *pa_source_list_to_string(pa_core *c) { t = pa_proplist_to_string_sep(source->proplist, "\n\t\t"); pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); + + if (source->ports) { + pa_device_port *p; + void *state; + + pa_strbuf_puts(s, "\tports:\n"); + PA_HASHMAP_FOREACH(p, source->ports, state) + pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority); + } + + if (source->active_port) + pa_strbuf_printf( + s, + "\tactive port: <%s>\n", + source->active_port->name); } return pa_strbuf_tostring_free(s); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index e9e2d601..b27346b2 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -3328,9 +3328,9 @@ static void command_set_volume( CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY); if (sink) - pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE); + pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE, TRUE); else if (source) - pa_source_set_volume(source, &volume); + pa_source_set_volume(source, &volume, TRUE); else if (si) pa_sink_input_set_volume(si, &volume, TRUE, TRUE); @@ -3400,9 +3400,9 @@ static void command_set_mute( CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY); if (sink) - pa_sink_set_mute(sink, mute); + pa_sink_set_mute(sink, mute, TRUE); else if (source) - pa_source_set_mute(source, mute); + pa_source_set_mute(source, mute, TRUE); else if (si) pa_sink_input_set_mute(si, mute, TRUE); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 0d05b00a..a5f96351 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -442,7 +442,7 @@ void pa_sink_input_unlink(pa_sink_input *i) { if (i->sink->flags & PA_SINK_FLAT_VOLUME) { pa_cvolume new_volume; pa_sink_update_flat_volume(i->sink, &new_volume); - pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE); } if (i->sink->asyncmsgq) @@ -520,7 +520,7 @@ void pa_sink_input_put(pa_sink_input *i) { if (i->sink->flags & PA_SINK_FLAT_VOLUME) { pa_cvolume new_volume; pa_sink_update_flat_volume(i->sink, &new_volume); - pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE); } else pa_sink_input_set_relative_volume(i, &i->virtual_volume); @@ -900,7 +900,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo * volumes and update the flat volume of the sink */ pa_sink_update_flat_volume(i->sink, &new_volume); - pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE); + pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE, FALSE); } else { @@ -1159,7 +1159,7 @@ int pa_sink_input_start_move(pa_sink_input *i) { /* We might need to update the sink's volume if we are in flat * volume mode. */ pa_sink_update_flat_volume(i->sink, &new_volume); - pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE); } pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); @@ -1252,7 +1252,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { /* We might need to update the sink's volume if we are in flat volume mode. */ pa_sink_update_flat_volume(i->sink, &new_volume); - pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE); } pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 1da094af..47792293 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -100,11 +100,51 @@ void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) { data->muted = !!mute; } +void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port) { + pa_assert(data); + + pa_xfree(data->active_port); + data->active_port = pa_xstrdup(port); +} + void pa_sink_new_data_done(pa_sink_new_data *data) { pa_assert(data); - pa_xfree(data->name); pa_proplist_free(data->proplist); + + if (data->ports) { + pa_device_port *p; + + while ((p = pa_hashmap_steal_first(data->ports))) + pa_device_port_free(p); + + pa_hashmap_free(data->ports, NULL, NULL); + } + + pa_xfree(data->name); + pa_xfree(data->active_port); +} + +pa_device_port *pa_device_port_new(const char *name, const char *description, size_t extra) { + pa_device_port *p; + + pa_assert(name); + + p = pa_xmalloc(PA_ALIGN(sizeof(pa_device_port)) + extra); + p->name = pa_xstrdup(name); + p->description = pa_xstrdup(description); + + p->priority = 0; + + return p; +} + +void pa_device_port_free(pa_device_port *p) { + pa_assert(p); + + pa_xfree(p->name); + pa_xfree(p->description); + pa_xfree(p); } /* Called from main context */ @@ -118,6 +158,7 @@ static void reset_callbacks(pa_sink *s) { s->set_mute = NULL; s->request_rewind = NULL; s->update_requested_latency = NULL; + s->set_port = NULL; } /* Called from main context */ @@ -152,6 +193,8 @@ pa_sink* pa_sink_new( return NULL; } + /* FIXME, need to free s here on failure */ + pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]); @@ -219,6 +262,30 @@ pa_sink* pa_sink_new( s->asyncmsgq = NULL; s->rtpoll = NULL; + /* As a minor optimization we just steal the list instead of + * copying it here */ + s->ports = data->ports; + data->ports = NULL; + + s->active_port = NULL; + s->save_port = FALSE; + + if (data->active_port && s->ports) + if ((s->active_port = pa_hashmap_get(s->ports, data->active_port))) + s->save_port = data->save_port; + + if (!s->active_port && s->ports) { + void *state; + pa_device_port *p; + + PA_HASHMAP_FOREACH(p, s->ports, state) + if (!s->active_port || p->priority > s->active_port->priority) + s->active_port = p; + } + + s->save_volume = data->save_volume; + s->save_muted = data->save_muted; + pa_silence_memchunk_get( &core->silence_cache, core->mempool, @@ -467,6 +534,15 @@ static void sink_free(pa_object *o) { if (s->proplist) pa_proplist_free(s->proplist); + if (s->ports) { + pa_device_port *p; + + while ((p = pa_hashmap_steal_first(s->ports))) + pa_device_port_free(p); + + pa_hashmap_free(s->ports, NULL, NULL); + } + pa_xfree(s); } @@ -485,6 +561,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) { pa_sink_assert_ref(s); s->rtpoll = p; + if (s->monitor_source) pa_source_set_rtpoll(s->monitor_source, p); } @@ -526,15 +603,15 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) { } /* Called from main context */ -pa_queue *pa_sink_move_all_start(pa_sink *s) { - pa_queue *q; +pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q) { pa_sink_input *i, *n; uint32_t idx; pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); - q = pa_queue_new(); + if (!q) + q = pa_queue_new(); for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) { n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)); @@ -1237,7 +1314,7 @@ void pa_sink_propagate_flat_volume(pa_sink *s) { } /* Called from main thread */ -void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference) { +void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save) { pa_bool_t virtual_volume_changed; pa_sink_assert_ref(s); @@ -1248,6 +1325,7 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume); s->virtual_volume = *volume; + s->save_volume = (!virtual_volume_changed && s->save_volume) || save; if (become_reference) s->reference_volume = s->virtual_volume; @@ -1318,15 +1396,17 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_boo } /* Called from main thread */ -void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) { +void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume, pa_bool_t save) { pa_sink_assert_ref(s); /* The sink implementor may call this if the volume changed to make sure everyone is notified */ - - if (pa_cvolume_equal(&s->virtual_volume, new_volume)) + if (pa_cvolume_equal(&s->virtual_volume, new_volume)) { + s->save_volume = s->save_volume || save; return; + } s->reference_volume = s->virtual_volume = *new_volume; + s->save_volume = save; if (s->flags & PA_SINK_FLAT_VOLUME) pa_sink_propagate_flat_volume(s); @@ -1335,7 +1415,7 @@ void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) { } /* Called from main thread */ -void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { +void pa_sink_set_mute(pa_sink *s, pa_bool_t mute, pa_bool_t save) { pa_bool_t old_muted; pa_sink_assert_ref(s); @@ -1343,6 +1423,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { old_muted = s->muted; s->muted = mute; + s->save_muted = (old_muted == s->muted && s->save_muted) || save; if (s->set_mute) s->set_mute(s); @@ -1378,15 +1459,19 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) { } /* Called from main thread */ -void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) { +void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save) { pa_sink_assert_ref(s); /* The sink implementor may call this if the volume changed to make sure everyone is notified */ - if (s->muted == new_muted) + if (s->muted == new_muted) { + s->save_muted = s->save_muted || save; return; + } s->muted = new_muted; + s->save_muted = save; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } @@ -1484,7 +1569,7 @@ unsigned pa_sink_check_suspend(pa_sink *s) { ret = 0; - for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { + PA_IDXSET_FOREACH(i, s->inputs, idx) { pa_sink_input_state_t st; st = pa_sink_input_get_state(i); @@ -2194,6 +2279,41 @@ size_t pa_sink_get_max_request(pa_sink *s) { return r; } +/* Called from main context */ +int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) { + pa_device_port *port; + + pa_assert(s); + + if (!s->set_port) { + pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name); + return -PA_ERR_NOTIMPLEMENTED; + } + + if (!s->ports) + return -PA_ERR_NOENTITY; + + if (!(port = pa_hashmap_get(s->ports, name))) + return -PA_ERR_NOENTITY; + + if (s->active_port == port) { + s->save_port = s->save_port || save; + return 0; + } + + if ((s->set_port(s, port)) < 0) + return -PA_ERR_NOENTITY; + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + + pa_log_info("Changed port of sink %u \"%s\" to %s", s->index, s->name, port->name); + + s->active_port = port; + s->save_port = save; + + return 0; +} + /* Called from main context */ pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) { const char *ff, *c, *t = NULL, *s = "", *profile, *bus; diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 0f32d163..d16fcc01 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -24,6 +24,7 @@ ***/ typedef struct pa_sink pa_sink; +typedef struct pa_device_port pa_device_port; #include @@ -49,11 +50,23 @@ static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) { return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED; } +struct pa_device_port { + char *name; + char *description; + + unsigned priority; + + /* .. followed by some implementation specific data */ +}; + +#define PA_DEVICE_PORT_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_device_port)))) + struct pa_sink { pa_msgobject parent; uint32_t index; pa_core *core; + pa_sink_state_t state; pa_sink_flags_t flags; pa_suspend_cause_t suspend_cause; @@ -83,6 +96,9 @@ struct pa_sink { pa_bool_t refresh_volume:1; pa_bool_t refresh_muted:1; + pa_bool_t save_port:1; + pa_bool_t save_volume:1; + pa_bool_t save_muted:1; pa_asyncmsgq *asyncmsgq; pa_rtpoll *rtpoll; @@ -91,6 +107,9 @@ struct pa_sink { pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */ + pa_hashmap *ports; + pa_device_port *active_port; + /* Called when the main loop requests a state change. Called from * main loop context. If returns -1 the state change will be * inhibited */ @@ -126,6 +145,10 @@ struct pa_sink { * thread context. */ void (*update_requested_latency)(pa_sink *s); /* dito */ + /* Called whenever the port shall be changed. Called from main + * thread. */ + int (*set_port)(pa_sink *s, pa_device_port *port); /* dito */ + /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ struct { @@ -192,6 +215,9 @@ typedef struct pa_sink_new_data { pa_module *module; pa_card *card; + pa_hashmap *ports; + char *active_port; + pa_sample_spec sample_spec; pa_channel_map channel_map; pa_cvolume volume; @@ -203,6 +229,10 @@ typedef struct pa_sink_new_data { pa_bool_t muted_is_set:1; pa_bool_t namereg_fail:1; + + pa_bool_t save_port:1; + pa_bool_t save_volume:1; + pa_bool_t save_muted:1; } pa_sink_new_data; pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data); @@ -211,6 +241,7 @@ void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_sp void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map); void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume); void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute); +void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port); void pa_sink_new_data_done(pa_sink_new_data *data); /*** To be called exclusively by the sink driver, from main context */ @@ -236,8 +267,8 @@ void pa_sink_detach(pa_sink *s); void pa_sink_attach(pa_sink *s); void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume); -void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume); -void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted); +void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume, pa_bool_t save); +void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save); pa_bool_t pa_device_init_description(pa_proplist *p); pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink); @@ -260,21 +291,23 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume); void pa_sink_propagate_flat_volume(pa_sink *s); -void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference); +void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save); const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh, pa_bool_t reference); -void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute); +void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute, pa_bool_t save); pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh); pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p); +int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save); + unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */ unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */ unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */ #define pa_sink_get_state(s) ((s)->state) /* Moves all inputs away, and stores them in pa_queue */ -pa_queue *pa_sink_move_all_start(pa_sink *s); +pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q); void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save); void pa_sink_move_all_fail(pa_queue *q); @@ -307,4 +340,7 @@ void pa_sink_invalidate_requested_latency(pa_sink *s); pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s); +pa_device_port *pa_device_port_new(const char *name, const char *description, size_t extra); +void pa_device_port_free(pa_device_port *p); + #endif diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index d35c8523..1e431160 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -93,11 +93,29 @@ void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) { data->muted = !!mute; } +void pa_source_new_data_set_port(pa_source_new_data *data, const char *port) { + pa_assert(data); + + pa_xfree(data->active_port); + data->active_port = pa_xstrdup(port); +} + void pa_source_new_data_done(pa_source_new_data *data) { pa_assert(data); - pa_xfree(data->name); pa_proplist_free(data->proplist); + + if (data->ports) { + pa_device_port *p; + + while ((p = pa_hashmap_steal_first(data->ports))) + pa_device_port_free(p); + + pa_hashmap_free(data->ports, NULL, NULL); + } + + pa_xfree(data->name); + pa_xfree(data->active_port); } /* Called from main context */ @@ -110,6 +128,7 @@ static void reset_callbacks(pa_source *s) { s->get_mute = NULL; s->set_mute = NULL; s->update_requested_latency = NULL; + s->set_port = NULL; } /* Called from main context */ @@ -142,6 +161,8 @@ pa_source* pa_source_new( return NULL; } + /* FIXME, need to free s here on failure */ + pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]); @@ -210,6 +231,30 @@ pa_source* pa_source_new( s->asyncmsgq = NULL; s->rtpoll = NULL; + /* As a minor optimization we just steal the list instead of + * copying it here */ + s->ports = data->ports; + data->ports = NULL; + + s->active_port = NULL; + s->save_port = FALSE; + + if (data->active_port && s->ports) + if ((s->active_port = pa_hashmap_get(s->ports, data->active_port))) + s->save_port = data->save_port; + + if (!s->active_port && s->ports) { + void *state; + pa_device_port *p; + + PA_HASHMAP_FOREACH(p, s->ports, state) + if (!s->active_port || p->priority > s->active_port->priority) + s->active_port = p; + } + + s->save_volume = data->save_volume; + s->save_muted = data->save_muted; + pa_silence_memchunk_get( &core->silence_cache, core->mempool, @@ -400,6 +445,15 @@ static void source_free(pa_object *o) { if (s->proplist) pa_proplist_free(s->proplist); + if (s->ports) { + pa_device_port *p; + + while ((p = pa_hashmap_steal_first(s->ports))) + pa_device_port_free(p); + + pa_hashmap_free(s->ports, NULL, NULL); + } + pa_xfree(s); } @@ -472,15 +526,15 @@ int pa_source_sync_suspend(pa_source *s) { } /* Called from main context */ -pa_queue *pa_source_move_all_start(pa_source *s) { - pa_queue *q; +pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q) { pa_source_output *o, *n; uint32_t idx; pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); - q = pa_queue_new(); + if (!q) + q = pa_queue_new(); for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) { n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)); @@ -667,7 +721,7 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) { } /* Called from main thread */ -void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { +void pa_source_set_volume(pa_source *s, const pa_cvolume *volume, pa_bool_t save) { pa_cvolume old_virtual_volume; pa_bool_t virtual_volume_changed; @@ -680,6 +734,7 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { old_virtual_volume = s->virtual_volume; s->virtual_volume = *volume; virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume); + s->save_volume = (!virtual_volume_changed && s->save_volume) || save; if (s->set_volume) { pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); @@ -725,20 +780,24 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) { } /* Called from main thread */ -void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume) { +void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume, pa_bool_t save) { pa_source_assert_ref(s); /* The source implementor may call this if the volume changed to make sure everyone is notified */ - if (pa_cvolume_equal(&s->virtual_volume, new_volume)) + if (pa_cvolume_equal(&s->virtual_volume, new_volume)) { + s->save_volume = s->save_volume || save; return; + } s->virtual_volume = *new_volume; + s->save_volume = save; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } /* Called from main thread */ -void pa_source_set_mute(pa_source *s, pa_bool_t mute) { +void pa_source_set_mute(pa_source *s, pa_bool_t mute, pa_bool_t save) { pa_bool_t old_muted; pa_source_assert_ref(s); @@ -746,6 +805,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) { old_muted = s->muted; s->muted = mute; + s->save_muted = (old_muted == s->muted && s->save_muted) || save; if (s->set_mute) s->set_mute(s); @@ -781,15 +841,19 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) { } /* Called from main thread */ -void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted) { +void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save) { pa_source_assert_ref(s); /* The source implementor may call this if the mute state changed to make sure everyone is notified */ - if (s->muted == new_muted) + if (s->muted == new_muted) { + s->save_muted = s->save_muted || save; return; + } s->muted = new_muted; + s->save_muted = save; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } @@ -866,7 +930,7 @@ unsigned pa_source_check_suspend(pa_source *s) { ret = 0; - for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx))) { + PA_IDXSET_FOREACH(o, s->outputs, idx) { pa_source_output_state_t st; st = pa_source_output_get_state(o); @@ -1323,3 +1387,38 @@ size_t pa_source_get_max_rewind(pa_source *s) { return r; } + +/* Called from main context */ +int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) { + pa_device_port *port; + + pa_assert(s); + + if (!s->set_port) { + pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name); + return -PA_ERR_NOTIMPLEMENTED; + } + + if (!s->ports) + return -PA_ERR_NOENTITY; + + if (!(port = pa_hashmap_get(s->ports, name))) + return -PA_ERR_NOENTITY; + + if (s->active_port == port) { + s->save_port = s->save_port || save; + return 0; + } + + if ((s->set_port(s, port)) < 0) + return -PA_ERR_NOENTITY; + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + + pa_log_info("Changed port of source %u \"%s\" to %s", s->index, s->name, port->name); + + s->active_port = port; + s->save_port = save; + + return 0; +} diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 1fbed70e..7e9fd8b7 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -56,6 +56,7 @@ struct pa_source { uint32_t index; pa_core *core; + pa_source_state_t state; pa_source_flags_t flags; pa_suspend_cause_t suspend_cause; @@ -83,6 +84,10 @@ struct pa_source { pa_bool_t refresh_volume:1; pa_bool_t refresh_muted:1; + pa_bool_t save_port:1; + pa_bool_t save_volume:1; + pa_bool_t save_muted:1; + pa_asyncmsgq *asyncmsgq; pa_rtpoll *rtpoll; @@ -90,6 +95,9 @@ struct pa_source { pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */ + pa_hashmap *ports; + pa_device_port *active_port; + /* Called when the main loop requests a state change. Called from * main loop context. If returns -1 the state change will be * inhibited */ @@ -121,6 +129,10 @@ struct pa_source { * thread context. */ void (*update_requested_latency)(pa_source *s); /* dito */ + /* Called whenever the port shall be changed. Called from main + * thread. */ + int (*set_port)(pa_source *s, pa_device_port *port); /*dito */ + /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ struct { @@ -174,6 +186,9 @@ typedef struct pa_source_new_data { pa_module *module; pa_card *card; + pa_hashmap *ports; + char *active_port; + pa_sample_spec sample_spec; pa_channel_map channel_map; pa_cvolume volume; @@ -185,6 +200,10 @@ typedef struct pa_source_new_data { pa_bool_t channel_map_is_set:1; pa_bool_t namereg_fail:1; + + pa_bool_t save_port:1; + pa_bool_t save_volume:1; + pa_bool_t save_muted:1; } pa_source_new_data; pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data); @@ -193,6 +212,7 @@ void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sampl void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map); void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume); void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute); +void pa_source_new_data_set_port(pa_source_new_data *data, const char *port); void pa_source_new_data_done(pa_source_new_data *data); /*** To be called exclusively by the source driver, from main context */ @@ -217,8 +237,8 @@ void pa_source_detach(pa_source *s); void pa_source_attach(pa_source *s); void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume); -void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume); -void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted); +void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume, pa_bool_t save); +void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save); int pa_source_sync_suspend(pa_source *s); @@ -235,20 +255,22 @@ int pa_source_update_status(pa_source*s); int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause); int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause); -void pa_source_set_volume(pa_source *source, const pa_cvolume *volume); +void pa_source_set_volume(pa_source *source, const pa_cvolume *volume, pa_bool_t save); const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh); -void pa_source_set_mute(pa_source *source, pa_bool_t mute); +void pa_source_set_mute(pa_source *source, pa_bool_t mute, pa_bool_t save); pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh); pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p); +int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save); + unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */ unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */ #define pa_source_get_state(s) ((pa_source_state_t) (s)->state) /* Moves all inputs away, and stores them in pa_queue */ -pa_queue *pa_source_move_all_start(pa_source *s); +pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q); void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save); void pa_source_move_all_fail(pa_queue *q); -- cgit From 334325efd7d8ba32ca3c4ba8f90dccae7abe914c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 04:17:25 +0200 Subject: alsa: allow placing device id in alsa device strings at arbitrary positions --- src/modules/alsa/alsa-mixer.c | 4 ++-- src/modules/alsa/alsa-util.c | 10 +++++----- src/modules/alsa/alsa-util.h | 6 +++--- src/modules/alsa/mixer/profile-sets/default.conf | 24 ++++++++++++------------ 4 files changed, 22 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 53875165..6f21e103 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -3179,7 +3179,7 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons try_ss = *ss; try_ss.channels = try_map.channels; - if (!(m ->output_pcm = pa_alsa_open_by_device_string_strv( + if (!(m ->output_pcm = pa_alsa_open_by_template( m->device_strings, dev_id, NULL, @@ -3203,7 +3203,7 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons try_ss = *ss; try_ss.channels = try_map.channels; - if (!(m ->input_pcm = pa_alsa_open_by_device_string_strv( + if (!(m ->input_pcm = pa_alsa_open_by_template( m->device_strings, dev_id, NULL, diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index d117ccd1..0204c28b 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -514,7 +514,7 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( try_ss.format = ss->format; try_map = m->channel_map; - pcm_handle = pa_alsa_open_by_device_string_strv( + pcm_handle = pa_alsa_open_by_template( m->device_strings, dev_id, dev, @@ -622,8 +622,8 @@ fail: return NULL; } -snd_pcm_t *pa_alsa_open_by_device_string_strv( - char **prefix, +snd_pcm_t *pa_alsa_open_by_template( + char **template, const char *dev_id, char **dev, pa_sample_spec *ss, @@ -639,10 +639,10 @@ snd_pcm_t *pa_alsa_open_by_device_string_strv( snd_pcm_t *pcm_handle; char **i; - for (i = prefix; *i; i++) { + for (i = template; *i; i++) { char *d; - d = pa_sprintf_malloc("%s:%s", *i, dev_id); + d = pa_replace(*i, "%f", dev_id); pcm_handle = pa_alsa_open_by_device_string( d, diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index 4993f454..c2f0e5b7 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -84,7 +84,7 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( /* Opens the explicit ALSA device */ snd_pcm_t *pa_alsa_open_by_device_string( - const char *device, + const char *dir, char **dev, /* modified at return */ pa_sample_spec *ss, /* modified at return */ pa_channel_map* map, /* modified at return */ @@ -97,8 +97,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( pa_bool_t require_exact_channel_number); /* Opens the explicit ALSA device with a fallback list */ -snd_pcm_t *pa_alsa_open_by_device_string_strv( - char **device, +snd_pcm_t *pa_alsa_open_by_template( + char **template, const char *dev_id, char **dev, /* modified at return */ pa_sample_spec *ss, /* modified at return */ diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf index bced10ad..bbe53410 100644 --- a/src/modules/alsa/mixer/profile-sets/default.conf +++ b/src/modules/alsa/mixer/profile-sets/default.conf @@ -22,49 +22,49 @@ auto-profiles = yes [Mapping analog-mono] -device-strings = hw +device-strings = hw:%f channel-map = mono paths-output = analog-output analog-output-headphones analog-output-mono analog-output-lfe-on-mono paths-input = analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line priority = 1 [Mapping analog-stereo] -device-strings = front hw +device-strings = front:%f hw:%f channel-map = left,right paths-output = analog-output analog-output-headphones analog-output-mono analog-output-lfe-on-mono paths-input = analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line priority = 10 [Mapping analog-surround-40] -device-strings = surround40 +device-strings = surround40:%f channel-map = front-left,front-right,rear-left,rear-right paths-output = analog-output analog-output-lfe-on-mono priority = 7 direction = output [Mapping analog-surround-41] -device-strings = surround41 +device-strings = surround41:%f channel-map = front-left,front-right,rear-left,rear-right,lfe paths-output = analog-output analog-output-lfe-on-mono priority = 8 direction = output [Mapping analog-surround-50] -device-strings = surround50 +device-strings = surround50:%f channel-map = front-left,front-right,rear-left,rear-right,front-center paths-output = analog-output analog-output-lfe-on-mono priority = 7 direction = output [Mapping analog-surround-51] -device-strings = surround51 +device-strings = surround51:%f channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe paths-output = analog-output analog-output-lfe-on-mono priority = 8 direction = output [Mapping analog-surround-71] -device-strings = surround71 +device-strings = surround71:%f channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right description = Analog Surround 7.1 paths-output = analog-output analog-output-lfe-on-mono @@ -72,29 +72,29 @@ priority = 7 direction = output [Mapping iec958-stereo] -device-strings = iec958 +device-strings = iec958:%f channel-map = left,right priority = 5 [Mapping iec958-surround-40] -device-strings = iec958 +device-strings = iec958:%f channel-map = front-left,front-right,rear-left,rear-right priority = 1 [Mapping iec958-ac3-surround-40] -device-strings = a52 +device-strings = a52:%f channel-map = front-left,front-right,rear-left,rear-right priority = 2 direction = output [Mapping iec958-ac3-surround-51] -device-strings = a52 +device-strings = a52:%f channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe priority = 3 direction = output [Mapping hdmi-stereo] -device-strings = hdmi +device-strings = hdmi:%f channel-map = left,right priority = 4 direction = output -- cgit From bd8e043a52834f3d3286ece03de46f498c9e241c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 04:51:57 +0200 Subject: bluetooth: return sensible error code in set_profile() --- src/modules/bluetooth/module-bluetooth-device.c | 6 +++--- src/pulse/def.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 9fec4edf..6bcd0b80 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1920,7 +1920,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { if (!(device = pa_bluetooth_discovery_get_by_path(u->discovery, u->path))) { pa_log_error("Failed to get device object."); - return -1; + return -PA_ERR_IO; } /* The state signal is sent by bluez, so it is racy to check @@ -1930,11 +1930,11 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { module will be unloaded. */ if (device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) { pa_log_warn("HSP is not connected, refused to switch profile"); - return -1; + return -PA_ERR_IO; } else if (device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP) { pa_log_warn("A2DP is not connected, refused to switch profile"); - return -1; + return -PA_ERR_IO; } if (u->sink) { diff --git a/src/pulse/def.h b/src/pulse/def.h index d5bbefe3..08399ca8 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -393,6 +393,7 @@ enum { PA_ERR_OBSOLETE, /**< Obsolete functionality. \since 0.9.15 */ PA_ERR_NOTIMPLEMENTED, /**< Missing implementation. \since 0.9.15 */ PA_ERR_FORKED, /**< The caller forked without calling execve() and tried to reuse the context. \since 0.9.15 */ + PA_ERR_IO, /**< An IO error happened. \since 0.9.16 */ PA_ERR_MAX /**< Not really an error but the first invalid error code */ }; -- cgit From 6d7cf14dbfc6600cea7ae5110af1160b6a6d140e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 04:52:41 +0200 Subject: native: implement command to change sink/source port --- src/pulsecore/native-common.h | 4 +++ src/pulsecore/protocol-native.c | 67 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index d4d7f3ee..f49abb09 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -165,6 +165,10 @@ enum { PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED, PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED, + /* Supported since protocol v16 (0.9.16) */ + PA_COMMAND_SET_SINK_PORT, + PA_COMMAND_SET_SOURCE_PORT, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index b27346b2..92efc9ee 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -284,6 +284,7 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_ERROR] = NULL, @@ -380,6 +381,9 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile, + [PA_COMMAND_SET_SINK_PORT] = command_set_sink_or_source_port, + [PA_COMMAND_SET_SOURCE_PORT] = command_set_sink_or_source_port, + [PA_COMMAND_EXTENSION] = command_extension }; @@ -4195,6 +4199,7 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_ uint32_t idx = PA_INVALID_INDEX; const char *name = NULL, *profile = NULL; pa_card *card = NULL; + int ret; pa_native_connection_assert_ref(c); pa_assert(t); @@ -4220,11 +4225,69 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_ CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY); - if (pa_card_set_profile(card, profile, TRUE) < 0) { - pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); + if ((ret = pa_card_set_profile(card, profile, TRUE)) < 0) { + pa_pstream_send_error(c->pstream, tag, -ret); + return; + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); + uint32_t idx = PA_INVALID_INDEX; + const char *name = NULL, *port = NULL; + int ret; + + pa_native_connection_assert_ref(c); + pa_assert(t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + pa_tagstruct_gets(t, &name) < 0 || + pa_tagstruct_gets(t, &port) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); return; } + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID); + + if (command == PA_COMMAND_SET_SINK_PORT) { + pa_sink *sink; + + if (idx != PA_INVALID_INDEX) + sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); + else + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); + + CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY); + + if ((ret = pa_sink_set_port(sink, port, TRUE)) < 0) { + pa_pstream_send_error(c->pstream, tag, -ret); + return; + } + } else { + pa_source *source; + + pa_assert(command = PA_COMMAND_SET_SOURCE_PORT); + + if (idx != PA_INVALID_INDEX) + source = pa_idxset_get_by_index(c->protocol->core->sources, idx); + else + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE); + + CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY); + + if ((ret = pa_source_set_port(source, port, TRUE)) < 0) { + pa_pstream_send_error(c->pstream, tag, -ret); + return; + } + } + pa_pstream_send_simple_ack(c->pstream, tag); } -- cgit From 914ef89e559c7b34159a0431b1e230da0eef96be Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 04:54:11 +0200 Subject: libpulse: implement client side for sink/source port selection commands --- src/map-file | 4 ++ src/pulse/introspect.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ src/pulse/introspect.h | 12 ++++++ 3 files changed, 116 insertions(+) (limited to 'src') diff --git a/src/map-file b/src/map-file index 6f8946c6..a2cc6c5d 100644 --- a/src/map-file +++ b/src/map-file @@ -96,10 +96,14 @@ pa_context_set_sink_input_mute; pa_context_set_sink_input_volume; pa_context_set_sink_mute_by_index; pa_context_set_sink_mute_by_name; +pa_context_set_sink_port_by_index; +pa_context_set_sink_port_by_name; pa_context_set_sink_volume_by_index; pa_context_set_sink_volume_by_name; pa_context_set_source_mute_by_index; pa_context_set_source_mute_by_name; +pa_context_set_source_port_by_index; +pa_context_set_source_port_by_name; pa_context_set_source_volume_by_index; pa_context_set_source_volume_by_name; pa_context_set_state_callback; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index ac8a11aa..0c45f99f 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -271,6 +271,56 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, return o; } +pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_PORT, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_tagstruct_puts(t, port); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char *name, const char*port, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_PORT, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_tagstruct_puts(t, port); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + /*** Source info ***/ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { @@ -406,6 +456,56 @@ pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name return o; } +pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_PORT, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_tagstruct_puts(t, port); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char *name, const char*port, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 16, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_PORT, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_tagstruct_puts(t, port); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + /*** Client info ***/ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 117880c8..87824239 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -248,6 +248,12 @@ pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_na /** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */ pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata); +/** Change the profile of a sink. \since 0.9.16 */ +pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata); + +/** Change the profile of a sink. \since 0.9.15 */ +pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata); + /** @} */ /** @{ \name Sources */ @@ -301,6 +307,12 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i /** Set the mute switch of a source device specified by its name */ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); +/** Change the profile of a source. \since 0.9.16 */ +pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata); + +/** Change the profile of a source. \since 0.9.15 */ +pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata); + /** @} */ /** @{ \name Server */ -- cgit From 6b2ca094ae18110271ef6facd963e5cf11ff1715 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 04:54:39 +0200 Subject: pactl: implement pactl set-{sink|source}-port --- src/utils/pactl.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 6608c01e..1ae15c75 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -49,8 +49,21 @@ static pa_context *context = NULL; static pa_mainloop_api *mainloop_api = NULL; -static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL, *module_name = NULL, *module_args = NULL, *card_name = NULL, *profile_name = NULL; -static uint32_t sink_input_idx = PA_INVALID_INDEX, source_output_idx = PA_INVALID_INDEX; +static char + *device = NULL, + *sample_name = NULL, + *sink_name = NULL, + *source_name = NULL, + *module_name = NULL, + *module_args = NULL, + *card_name = NULL, + *profile_name = NULL, + *port_name = NULL; + +static uint32_t + sink_input_idx = PA_INVALID_INDEX, + source_output_idx = PA_INVALID_INDEX; + static uint32_t module_index; static pa_bool_t suspend; @@ -80,7 +93,9 @@ static enum { UNLOAD_MODULE, SUSPEND_SINK, SUSPEND_SOURCE, - SET_CARD_PROFILE + SET_CARD_PROFILE, + SET_SINK_PORT, + SET_SOURCE_PORT } action = NONE; static void quit(int ret) { @@ -753,6 +768,14 @@ static void context_state_callback(pa_context *c, void *userdata) { pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL)); break; + case SET_SINK_PORT: + pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL)); + break; + + case SET_SOURCE_PORT: + pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL)); + break; + default: pa_assert_not_reached(); } @@ -788,12 +811,14 @@ static void help(const char *argv0) { "%s [options] unload-module ID\n" "%s [options] suspend-sink [SINK] 1|0\n" "%s [options] suspend-source [SOURCE] 1|0\n" - "%s [options] set-card-profile [CARD] [PROFILE] \n\n" + "%s [options] set-card-profile [CARD] [PROFILE] \n" + "%s [options] set-sink-port [SINK] [PORT] \n" + "%s [options] set-source-port [SOURCE] [PORT] \n\n" " -h, --help Show this help\n" " --version Show version\n\n" " -s, --server=SERVER The name of the server to connect to\n" " -n, --client-name=NAME How to call this client on the server\n"), - argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0); + argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0); } enum { @@ -1017,6 +1042,28 @@ int main(int argc, char *argv[]) { card_name = pa_xstrdup(argv[optind+1]); profile_name = pa_xstrdup(argv[optind+2]); + } else if (pa_streq(argv[optind], "set-sink-port")) { + action = SET_SINK_PORT; + + if (argc != optind+3) { + pa_log(_("You have to specify a sink name/index and a port name\n")); + goto quit; + } + + sink_name = pa_xstrdup(argv[optind+1]); + port_name = pa_xstrdup(argv[optind+2]); + + } else if (pa_streq(argv[optind], "set-source-port")) { + action = SET_SOURCE_PORT; + + if (argc != optind+3) { + pa_log(_("You have to specify a source name/index and a port name\n")); + goto quit; + } + + source_name = pa_xstrdup(argv[optind+1]); + port_name = pa_xstrdup(argv[optind+2]); + } else if (pa_streq(argv[optind], "help")) { help(bn); ret = 0; -- cgit From c65ebeec1e3eafef453ff9d936af4d314034c5c3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 15:09:28 +0200 Subject: raop: move all raop files to subdir --- src/Makefile.am | 8 +- src/modules/module-raop-discover.c | 397 ------------------ src/modules/module-raop-sink.c | 697 -------------------------------- src/modules/raop/module-raop-discover.c | 397 ++++++++++++++++++ src/modules/raop/module-raop-sink.c | 697 ++++++++++++++++++++++++++++++++ 5 files changed, 1098 insertions(+), 1098 deletions(-) delete mode 100644 src/modules/module-raop-discover.c delete mode 100644 src/modules/module-raop-sink.c create mode 100644 src/modules/raop/module-raop-discover.c create mode 100644 src/modules/raop/module-raop-sink.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 5d8487ae..40b56757 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1205,8 +1205,8 @@ SYMDEF_FILES = \ modules/bluetooth/module-bluetooth-proximity-symdef.h \ modules/bluetooth/module-bluetooth-discover-symdef.h \ modules/bluetooth/module-bluetooth-device-symdef.h \ - modules/module-raop-sink-symdef.h \ - modules/module-raop-discover-symdef.h \ + modules/raop/module-raop-sink-symdef.h \ + modules/raop/module-raop-discover-symdef.h \ modules/gconf/module-gconf-symdef.h \ modules/module-position-event-sounds-symdef.h \ modules/module-augment-properties-symdef.h \ @@ -1626,11 +1626,11 @@ module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_M module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) # Apple Airtunes/RAOP -module_raop_sink_la_SOURCES = modules/module-raop-sink.c +module_raop_sink_la_SOURCES = modules/raop/module-raop-sink.c module_raop_sink_la_LDFLAGS = $(MODULE_LDFLAGS) module_raop_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libraop.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la -module_raop_discover_la_SOURCES = modules/module-raop-discover.c +module_raop_discover_la_SOURCES = modules/raop/module-raop-discover.c module_raop_discover_la_LDFLAGS = $(MODULE_LDFLAGS) module_raop_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_raop_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS) diff --git a/src/modules/module-raop-discover.c b/src/modules/module-raop-discover.c deleted file mode 100644 index eaeb77fc..00000000 --- a/src/modules/module-raop-discover.c +++ /dev/null @@ -1,397 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - Copyright 2008 Colin Guthrie - - 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, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "module-raop-discover-symdef.h" - -PA_MODULE_AUTHOR("Colin Guthrie"); -PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery of RAOP devices"); -PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(TRUE); - -#define SERVICE_TYPE_SINK "_raop._tcp" - -static const char* const valid_modargs[] = { - NULL -}; - -struct tunnel { - AvahiIfIndex interface; - AvahiProtocol protocol; - char *name, *type, *domain; - uint32_t module_index; -}; - -struct userdata { - pa_core *core; - pa_module *module; - AvahiPoll *avahi_poll; - AvahiClient *client; - AvahiServiceBrowser *sink_browser; - - pa_hashmap *tunnels; -}; - -static unsigned tunnel_hash(const void *p) { - const struct tunnel *t = p; - - return - (unsigned) t->interface + - (unsigned) t->protocol + - pa_idxset_string_hash_func(t->name) + - pa_idxset_string_hash_func(t->type) + - pa_idxset_string_hash_func(t->domain); -} - -static int tunnel_compare(const void *a, const void *b) { - const struct tunnel *ta = a, *tb = b; - int r; - - if (ta->interface != tb->interface) - return 1; - if (ta->protocol != tb->protocol) - return 1; - if ((r = strcmp(ta->name, tb->name))) - return r; - if ((r = strcmp(ta->type, tb->type))) - return r; - if ((r = strcmp(ta->domain, tb->domain))) - return r; - - return 0; -} - -static struct tunnel *tunnel_new( - AvahiIfIndex interface, AvahiProtocol protocol, - const char *name, const char *type, const char *domain) { - - struct tunnel *t; - t = pa_xnew(struct tunnel, 1); - t->interface = interface; - t->protocol = protocol; - t->name = pa_xstrdup(name); - t->type = pa_xstrdup(type); - t->domain = pa_xstrdup(domain); - t->module_index = PA_IDXSET_INVALID; - return t; -} - -static void tunnel_free(struct tunnel *t) { - pa_assert(t); - pa_xfree(t->name); - pa_xfree(t->type); - pa_xfree(t->domain); - pa_xfree(t); -} - -static void resolver_cb( - AvahiServiceResolver *r, - AvahiIfIndex interface, AvahiProtocol protocol, - AvahiResolverEvent event, - const char *name, const char *type, const char *domain, - const char *host_name, const AvahiAddress *a, uint16_t port, - AvahiStringList *txt, - AvahiLookupResultFlags flags, - void *userdata) { - - struct userdata *u = userdata; - struct tunnel *tnl; - - pa_assert(u); - - tnl = tunnel_new(interface, protocol, name, type, domain); - - if (event != AVAHI_RESOLVER_FOUND) - pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client))); - else { - char *device = NULL, *nicename, *dname, *vname, *args; - char at[AVAHI_ADDRESS_STR_MAX]; - AvahiStringList *l; - pa_module *m; - - if ((nicename = strstr(name, "@"))) { - ++nicename; - if (strlen(nicename) > 0) { - pa_log_debug("Found RAOP: %s", nicename); - } - } - - for (l = txt; l; l = l->next) { - char *key, *value; - pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0); - - pa_log_debug("Found key: '%s' with value: '%s'", key, value); - if (strcmp(key, "device") == 0) { - pa_xfree(device); - device = value; - value = NULL; - } - avahi_free(key); - avahi_free(value); - } - - if (device) - dname = pa_sprintf_malloc("raop.%s.%s", host_name, device); - else - dname = pa_sprintf_malloc("raop.%s", host_name); - - if (!(vname = pa_namereg_make_valid_name(dname))) { - pa_log("Cannot construct valid device name from '%s'.", dname); - avahi_free(device); - pa_xfree(dname); - goto finish; - } - pa_xfree(dname); - - /* - TODO: allow this syntax of server name in things.... - args = pa_sprintf_malloc("server=[%s]:%u " - "sink_name=%s", - avahi_address_snprint(at, sizeof(at), a), port, - vname);*/ - if (nicename) { - args = pa_sprintf_malloc("server=%s " - "sink_name=%s " - "description=\"%s\"", - avahi_address_snprint(at, sizeof(at), a), - vname, - nicename); - - } else { - args = pa_sprintf_malloc("server=%s " - "sink_name=%s", - avahi_address_snprint(at, sizeof(at), a), - vname); - } - - pa_log_debug("Loading module-raop-sink with arguments '%s'", args); - - if ((m = pa_module_load(u->core, "module-raop-sink", args))) { - tnl->module_index = m->index; - pa_hashmap_put(u->tunnels, tnl, tnl); - tnl = NULL; - } - - pa_xfree(vname); - pa_xfree(args); - avahi_free(device); - } - -finish: - - avahi_service_resolver_free(r); - - if (tnl) - tunnel_free(tnl); -} - -static void browser_cb( - AvahiServiceBrowser *b, - AvahiIfIndex interface, AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *name, const char *type, const char *domain, - AvahiLookupResultFlags flags, - void *userdata) { - - struct userdata *u = userdata; - struct tunnel *t; - - pa_assert(u); - - if (flags & AVAHI_LOOKUP_RESULT_LOCAL) - return; - - t = tunnel_new(interface, protocol, name, type, domain); - - if (event == AVAHI_BROWSER_NEW) { - - if (!pa_hashmap_get(u->tunnels, t)) - if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u))) - pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client))); - - /* We ignore the returned resolver object here, since the we don't - * need to attach any special data to it, and we can still destroy - * it from the callback */ - - } else if (event == AVAHI_BROWSER_REMOVE) { - struct tunnel *t2; - - if ((t2 = pa_hashmap_get(u->tunnels, t))) { - pa_module_unload_by_index(u->core, t2->module_index, TRUE); - pa_hashmap_remove(u->tunnels, t2); - tunnel_free(t2); - } - } - - tunnel_free(t); -} - -static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) { - struct userdata *u = userdata; - - pa_assert(c); - pa_assert(u); - - u->client = c; - - switch (state) { - case AVAHI_CLIENT_S_REGISTERING: - case AVAHI_CLIENT_S_RUNNING: - case AVAHI_CLIENT_S_COLLISION: - - if (!u->sink_browser) { - - if (!(u->sink_browser = avahi_service_browser_new( - c, - AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, - SERVICE_TYPE_SINK, - NULL, - 0, - browser_cb, u))) { - - pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c))); - pa_module_unload_request(u->module, TRUE); - } - } - - break; - - case AVAHI_CLIENT_FAILURE: - if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) { - int error; - - pa_log_debug("Avahi daemon disconnected."); - - if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) { - pa_log("avahi_client_new() failed: %s", avahi_strerror(error)); - pa_module_unload_request(u->module, TRUE); - } - } - - /* Fall through */ - - case AVAHI_CLIENT_CONNECTING: - - if (u->sink_browser) { - avahi_service_browser_free(u->sink_browser); - u->sink_browser = NULL; - } - - break; - - default: ; - } -} - -int pa__init(pa_module*m) { - - struct userdata *u; - pa_modargs *ma = NULL; - int error; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("Failed to parse module arguments."); - goto fail; - } - - m->userdata = u = pa_xnew(struct userdata, 1); - u->core = m->core; - u->module = m; - u->sink_browser = NULL; - - u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare); - - u->avahi_poll = pa_avahi_poll_new(m->core->mainloop); - - if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) { - pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error)); - goto fail; - } - - pa_modargs_free(ma); - - return 0; - -fail: - pa__done(m); - - if (ma) - pa_modargs_free(ma); - - return -1; -} - -void pa__done(pa_module*m) { - struct userdata*u; - pa_assert(m); - - if (!(u = m->userdata)) - return; - - if (u->client) - avahi_client_free(u->client); - - if (u->avahi_poll) - pa_avahi_poll_free(u->avahi_poll); - - if (u->tunnels) { - struct tunnel *t; - - while ((t = pa_hashmap_steal_first(u->tunnels))) { - pa_module_unload_by_index(u->core, t->module_index, TRUE); - tunnel_free(t); - } - - pa_hashmap_free(u->tunnels, NULL, NULL); - } - - pa_xfree(u); -} diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c deleted file mode 100644 index 54de42c2..00000000 --- a/src/modules/module-raop-sink.c +++ /dev/null @@ -1,697 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - Copyright 2008 Colin Guthrie - - 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, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_LINUX_SOCKIOS_H -#include -#endif - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "module-raop-sink-symdef.h" -#include "rtp.h" -#include "sdp.h" -#include "sap.h" -#include "raop_client.h" - -PA_MODULE_AUTHOR("Colin Guthrie"); -PA_MODULE_DESCRIPTION("RAOP Sink"); -PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(FALSE); -PA_MODULE_USAGE( - "sink_name= " - "sink_properties= " - "server=
" - "format= " - "rate= " - "channels="); - -#define DEFAULT_SINK_NAME "raop" - -struct userdata { - pa_core *core; - pa_module *module; - pa_sink *sink; - - pa_thread_mq thread_mq; - pa_rtpoll *rtpoll; - pa_rtpoll_item *rtpoll_item; - pa_thread *thread; - - pa_memchunk raw_memchunk; - pa_memchunk encoded_memchunk; - - void *write_data; - size_t write_length, write_index; - - void *read_data; - size_t read_length, read_index; - - pa_usec_t latency; - - /*esd_format_t format;*/ - int32_t rate; - - pa_smoother *smoother; - int fd; - - int64_t offset; - int64_t encoding_overhead; - int32_t next_encoding_overhead; - double encoding_ratio; - - pa_raop_client *raop; - - size_t block_size; -}; - -static const char* const valid_modargs[] = { - "sink_name", - "sink_properties", - "server", - "format", - "rate", - "channels", - "description", /* supported for compatibility reasons, made redundant by sink_properties= */ - NULL -}; - -enum { - SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX, - SINK_MESSAGE_RIP_SOCKET -}; - -/* Forward declaration */ -static void sink_set_volume_cb(pa_sink *); - -static void on_connection(int fd, void*userdata) { - int so_sndbuf = 0; - socklen_t sl = sizeof(int); - struct userdata *u = userdata; - pa_assert(u); - - pa_assert(u->fd < 0); - u->fd = fd; - - if (getsockopt(u->fd, SOL_SOCKET, SO_SNDBUF, &so_sndbuf, &sl) < 0) - pa_log_warn("getsockopt(SO_SNDBUF) failed: %s", pa_cstrerror(errno)); - else { - pa_log_debug("SO_SNDBUF is %zu.", (size_t) so_sndbuf); - pa_sink_set_max_request(u->sink, PA_MAX((size_t) so_sndbuf, u->block_size)); - } - - /* Set the initial volume */ - sink_set_volume_cb(u->sink); - - pa_log_debug("Connection authenticated, handing fd to IO thread..."); - - pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL); -} - -static void on_close(void*userdata) { - struct userdata *u = userdata; - pa_assert(u); - - pa_log_debug("Connection closed, informing IO thread..."); - - pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RIP_SOCKET, NULL, 0, NULL, NULL); -} - -static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { - struct userdata *u = PA_SINK(o)->userdata; - - switch (code) { - - case PA_SINK_MESSAGE_SET_STATE: - - switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { - - case PA_SINK_SUSPENDED: - pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); - - pa_smoother_pause(u->smoother, pa_rtclock_usec()); - - /* Issue a FLUSH if we are connected */ - if (u->fd >= 0) { - pa_raop_flush(u->raop); - } - break; - - case PA_SINK_IDLE: - case PA_SINK_RUNNING: - - if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { - pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE); - - /* The connection can be closed when idle, so check to - see if we need to reestablish it */ - if (u->fd < 0) - pa_raop_connect(u->raop); - else - pa_raop_flush(u->raop); - } - - break; - - case PA_SINK_UNLINKED: - case PA_SINK_INIT: - case PA_SINK_INVALID_STATE: - ; - } - - break; - - case PA_SINK_MESSAGE_GET_LATENCY: { - pa_usec_t w, r; - - r = pa_smoother_get(u->smoother, pa_rtclock_usec()); - w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec); - - *((pa_usec_t*) data) = w > r ? w - r : 0; - return 0; - } - - case SINK_MESSAGE_PASS_SOCKET: { - struct pollfd *pollfd; - - pa_assert(!u->rtpoll_item); - - u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); - pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); - pollfd->fd = u->fd; - pollfd->events = POLLOUT; - /*pollfd->events = */pollfd->revents = 0; - - if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { - /* Our stream has been suspended so we just flush it.... */ - pa_raop_flush(u->raop); - } - return 0; - } - - case SINK_MESSAGE_RIP_SOCKET: { - pa_assert(u->fd >= 0); - - pa_close(u->fd); - u->fd = -1; - - if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { - - pa_log_debug("RTSP control connection closed, but we're suspended so let's not worry about it... we'll open it again later"); - - if (u->rtpoll_item) - pa_rtpoll_item_free(u->rtpoll_item); - u->rtpoll_item = NULL; - } else { - /* Quesiton: is this valid here: or should we do some sort of: - return pa_sink_process_msg(PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL); - ?? */ - pa_module_unload_request(u->module, TRUE); - } - return 0; - } - } - - return pa_sink_process_msg(o, code, data, offset, chunk); -} - -static void sink_set_volume_cb(pa_sink *s) { - struct userdata *u = s->userdata; - pa_cvolume hw; - pa_volume_t v; - char t[PA_CVOLUME_SNPRINT_MAX]; - - pa_assert(u); - - /* If we're muted we don't need to do anything */ - if (s->muted) - return; - - /* Calculate the max volume of all channels. - We'll use this as our (single) volume on the APEX device and emulate - any variation in channel volumes in software */ - v = pa_cvolume_max(&s->virtual_volume); - - /* Create a pa_cvolume version of our single value */ - pa_cvolume_set(&hw, s->sample_spec.channels, v); - - /* Perform any software manipulation of the volume needed */ - pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &hw); - - pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); - pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &hw)); - pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); - - /* Any necessary software volume manipulateion is done so set - our hw volume (or v as a single value) on the device */ - pa_raop_client_set_volume(u->raop, v); -} - -static void sink_set_mute_cb(pa_sink *s) { - struct userdata *u = s->userdata; - - pa_assert(u); - - if (s->muted) { - pa_raop_client_set_volume(u->raop, PA_VOLUME_MUTED); - } else { - sink_set_volume_cb(s); - } -} - -static void thread_func(void *userdata) { - struct userdata *u = userdata; - int write_type = 0; - pa_memchunk silence; - uint32_t silence_overhead = 0; - double silence_ratio = 0; - - pa_assert(u); - - pa_log_debug("Thread starting up"); - - pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); - - pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); - - /* Create a chunk of memory that is our encoded silence sample. */ - pa_memchunk_reset(&silence); - - for (;;) { - int ret; - - if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) - if (u->sink->thread_info.rewind_requested) - pa_sink_process_rewind(u->sink, 0); - - if (u->rtpoll_item) { - struct pollfd *pollfd; - pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); - - /* Render some data and write it to the fifo */ - if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) { - pa_usec_t usec; - int64_t n; - void *p; - - if (!silence.memblock) { - pa_memchunk silence_tmp; - - pa_memchunk_reset(&silence_tmp); - silence_tmp.memblock = pa_memblock_new(u->core->mempool, 4096); - silence_tmp.length = 4096; - p = pa_memblock_acquire(silence_tmp.memblock); - memset(p, 0, 4096); - pa_memblock_release(silence_tmp.memblock); - pa_raop_client_encode_sample(u->raop, &silence_tmp, &silence); - pa_assert(0 == silence_tmp.length); - silence_overhead = silence_tmp.length - 4096; - silence_ratio = silence_tmp.length / 4096; - pa_memblock_unref(silence_tmp.memblock); - } - - for (;;) { - ssize_t l; - - if (u->encoded_memchunk.length <= 0) { - if (u->encoded_memchunk.memblock) - pa_memblock_unref(u->encoded_memchunk.memblock); - if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { - size_t rl; - - /* We render real data */ - if (u->raw_memchunk.length <= 0) { - if (u->raw_memchunk.memblock) - pa_memblock_unref(u->raw_memchunk.memblock); - pa_memchunk_reset(&u->raw_memchunk); - - /* Grab unencoded data */ - pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); - } - pa_assert(u->raw_memchunk.length > 0); - - /* Encode it */ - rl = u->raw_memchunk.length; - u->encoding_overhead += u->next_encoding_overhead; - pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk); - u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); - u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); - } else { - /* We render some silence into our memchunk */ - memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk)); - pa_memblock_ref(silence.memblock); - - /* Calculate/store some values to be used with the smoother */ - u->next_encoding_overhead = silence_overhead; - u->encoding_ratio = silence_ratio; - } - } - pa_assert(u->encoded_memchunk.length > 0); - - p = pa_memblock_acquire(u->encoded_memchunk.memblock); - l = pa_write(u->fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type); - pa_memblock_release(u->encoded_memchunk.memblock); - - pa_assert(l != 0); - - if (l < 0) { - - if (errno == EINTR) - continue; - else if (errno == EAGAIN) { - - /* OK, we filled all socket buffers up - * now. */ - goto filled_up; - - } else { - pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); - goto fail; - } - - } else { - u->offset += l; - - u->encoded_memchunk.index += l; - u->encoded_memchunk.length -= l; - - pollfd->revents = 0; - - if (u->encoded_memchunk.length > 0) { - /* we've completely written the encoded data, so update our overhead */ - u->encoding_overhead += u->next_encoding_overhead; - - /* OK, we wrote less that we asked for, - * hence we can assume that the socket - * buffers are full now */ - goto filled_up; - } - } - } - - filled_up: - - /* At this spot we know that the socket buffers are - * fully filled up. This is the best time to estimate - * the playback position of the server */ - - n = u->offset - u->encoding_overhead; - -#ifdef SIOCOUTQ - { - int l; - if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0) - n -= (l / u->encoding_ratio); - } -#endif - - usec = pa_bytes_to_usec(n, &u->sink->sample_spec); - - if (usec > u->latency) - usec -= u->latency; - else - usec = 0; - - pa_smoother_put(u->smoother, pa_rtclock_usec(), usec); - } - - /* Hmm, nothing to do. Let's sleep */ - pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;*/ - } - - if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) - goto fail; - - if (ret == 0) - goto finish; - - if (u->rtpoll_item) { - struct pollfd* pollfd; - - pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); - - if (pollfd->revents & ~POLLOUT) { - if (u->sink->thread_info.state != PA_SINK_SUSPENDED) { - pa_log("FIFO shutdown."); - goto fail; - } - - /* We expect this to happen on occasion if we are not sending data. - It's perfectly natural and normal and natural */ - if (u->rtpoll_item) - pa_rtpoll_item_free(u->rtpoll_item); - u->rtpoll_item = NULL; - } - } - } - -fail: - /* If this was no regular exit from the loop we have to continue - * processing messages until we received PA_MESSAGE_SHUTDOWN */ - pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); - pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); - -finish: - if (silence.memblock) - pa_memblock_unref(silence.memblock); - pa_log_debug("Thread shutting down"); -} - -int pa__init(pa_module*m) { - struct userdata *u = NULL; - pa_sample_spec ss; - pa_modargs *ma = NULL; - const char *server, *desc; - pa_sink_new_data data; - - pa_assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("failed to parse module arguments"); - goto fail; - } - - ss = m->core->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log("invalid sample format specification"); - goto fail; - } - - if ((/*ss.format != PA_SAMPLE_U8 &&*/ ss.format != PA_SAMPLE_S16NE) || - (ss.channels > 2)) { - pa_log("sample type support is limited to mono/stereo and U8 or S16NE sample data"); - goto fail; - } - - u = pa_xnew0(struct userdata, 1); - u->core = m->core; - u->module = m; - m->userdata = u; - u->fd = -1; - u->smoother = pa_smoother_new( - PA_USEC_PER_SEC, - PA_USEC_PER_SEC*2, - TRUE, - TRUE, - 10, - 0, - FALSE); - pa_memchunk_reset(&u->raw_memchunk); - pa_memchunk_reset(&u->encoded_memchunk); - u->offset = 0; - u->encoding_overhead = 0; - u->next_encoding_overhead = 0; - u->encoding_ratio = 1.0; - - u->rtpoll = pa_rtpoll_new(); - pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); - u->rtpoll_item = NULL; - - /*u->format = - (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) | - (ss.channels == 2 ? ESD_STEREO : ESD_MONO);*/ - u->rate = ss.rate; - u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss); - - u->read_data = u->write_data = NULL; - u->read_index = u->write_index = u->read_length = u->write_length = 0; - - /*u->state = STATE_AUTH;*/ - u->latency = 0; - - if (!(server = pa_modargs_get_value(ma, "server", NULL))) { - pa_log("No server argument given."); - goto fail; - } - - pa_sink_new_data_init(&data); - data.driver = __FILE__; - data.module = m; - pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); - pa_sink_new_data_set_sample_spec(&data, &ss); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "music"); - if ((desc = pa_modargs_get_value(ma, "description", NULL))) - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, desc); - else - pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "RAOP sink '%s'", server); - - if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { - pa_log("Invalid properties"); - pa_sink_new_data_done(&data); - goto fail; - } - - u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK); - pa_sink_new_data_done(&data); - - if (!u->sink) { - pa_log("Failed to create sink."); - goto fail; - } - - u->sink->parent.process_msg = sink_process_msg; - u->sink->userdata = u; - u->sink->set_volume = sink_set_volume_cb; - u->sink->set_mute = sink_set_mute_cb; - u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL; - - pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); - pa_sink_set_rtpoll(u->sink, u->rtpoll); - - if (!(u->raop = pa_raop_client_new(u->core, server))) { - pa_log("Failed to connect to server."); - goto fail; - } - - pa_raop_client_set_callback(u->raop, on_connection, u); - pa_raop_client_set_closed_callback(u->raop, on_close, u); - - if (!(u->thread = pa_thread_new(thread_func, u))) { - pa_log("Failed to create thread."); - goto fail; - } - - pa_sink_put(u->sink); - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(m); - - return -1; -} - -int pa__get_n_used(pa_module *m) { - struct userdata *u; - - pa_assert(m); - pa_assert_se(u = m->userdata); - - return pa_sink_linked_by(u->sink); -} - -void pa__done(pa_module*m) { - struct userdata *u; - pa_assert(m); - - if (!(u = m->userdata)) - return; - - if (u->sink) - pa_sink_unlink(u->sink); - - if (u->thread) { - pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); - pa_thread_free(u->thread); - } - - pa_thread_mq_done(&u->thread_mq); - - if (u->sink) - pa_sink_unref(u->sink); - - if (u->rtpoll_item) - pa_rtpoll_item_free(u->rtpoll_item); - - if (u->rtpoll) - pa_rtpoll_free(u->rtpoll); - - if (u->raw_memchunk.memblock) - pa_memblock_unref(u->raw_memchunk.memblock); - - if (u->encoded_memchunk.memblock) - pa_memblock_unref(u->encoded_memchunk.memblock); - - if (u->raop) - pa_raop_client_free(u->raop); - - pa_xfree(u->read_data); - pa_xfree(u->write_data); - - if (u->smoother) - pa_smoother_free(u->smoother); - - if (u->fd >= 0) - pa_close(u->fd); - - pa_xfree(u); -} diff --git a/src/modules/raop/module-raop-discover.c b/src/modules/raop/module-raop-discover.c new file mode 100644 index 00000000..eaeb77fc --- /dev/null +++ b/src/modules/raop/module-raop-discover.c @@ -0,0 +1,397 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2008 Colin Guthrie + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-raop-discover-symdef.h" + +PA_MODULE_AUTHOR("Colin Guthrie"); +PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery of RAOP devices"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); + +#define SERVICE_TYPE_SINK "_raop._tcp" + +static const char* const valid_modargs[] = { + NULL +}; + +struct tunnel { + AvahiIfIndex interface; + AvahiProtocol protocol; + char *name, *type, *domain; + uint32_t module_index; +}; + +struct userdata { + pa_core *core; + pa_module *module; + AvahiPoll *avahi_poll; + AvahiClient *client; + AvahiServiceBrowser *sink_browser; + + pa_hashmap *tunnels; +}; + +static unsigned tunnel_hash(const void *p) { + const struct tunnel *t = p; + + return + (unsigned) t->interface + + (unsigned) t->protocol + + pa_idxset_string_hash_func(t->name) + + pa_idxset_string_hash_func(t->type) + + pa_idxset_string_hash_func(t->domain); +} + +static int tunnel_compare(const void *a, const void *b) { + const struct tunnel *ta = a, *tb = b; + int r; + + if (ta->interface != tb->interface) + return 1; + if (ta->protocol != tb->protocol) + return 1; + if ((r = strcmp(ta->name, tb->name))) + return r; + if ((r = strcmp(ta->type, tb->type))) + return r; + if ((r = strcmp(ta->domain, tb->domain))) + return r; + + return 0; +} + +static struct tunnel *tunnel_new( + AvahiIfIndex interface, AvahiProtocol protocol, + const char *name, const char *type, const char *domain) { + + struct tunnel *t; + t = pa_xnew(struct tunnel, 1); + t->interface = interface; + t->protocol = protocol; + t->name = pa_xstrdup(name); + t->type = pa_xstrdup(type); + t->domain = pa_xstrdup(domain); + t->module_index = PA_IDXSET_INVALID; + return t; +} + +static void tunnel_free(struct tunnel *t) { + pa_assert(t); + pa_xfree(t->name); + pa_xfree(t->type); + pa_xfree(t->domain); + pa_xfree(t); +} + +static void resolver_cb( + AvahiServiceResolver *r, + AvahiIfIndex interface, AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, const char *type, const char *domain, + const char *host_name, const AvahiAddress *a, uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + void *userdata) { + + struct userdata *u = userdata; + struct tunnel *tnl; + + pa_assert(u); + + tnl = tunnel_new(interface, protocol, name, type, domain); + + if (event != AVAHI_RESOLVER_FOUND) + pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client))); + else { + char *device = NULL, *nicename, *dname, *vname, *args; + char at[AVAHI_ADDRESS_STR_MAX]; + AvahiStringList *l; + pa_module *m; + + if ((nicename = strstr(name, "@"))) { + ++nicename; + if (strlen(nicename) > 0) { + pa_log_debug("Found RAOP: %s", nicename); + } + } + + for (l = txt; l; l = l->next) { + char *key, *value; + pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0); + + pa_log_debug("Found key: '%s' with value: '%s'", key, value); + if (strcmp(key, "device") == 0) { + pa_xfree(device); + device = value; + value = NULL; + } + avahi_free(key); + avahi_free(value); + } + + if (device) + dname = pa_sprintf_malloc("raop.%s.%s", host_name, device); + else + dname = pa_sprintf_malloc("raop.%s", host_name); + + if (!(vname = pa_namereg_make_valid_name(dname))) { + pa_log("Cannot construct valid device name from '%s'.", dname); + avahi_free(device); + pa_xfree(dname); + goto finish; + } + pa_xfree(dname); + + /* + TODO: allow this syntax of server name in things.... + args = pa_sprintf_malloc("server=[%s]:%u " + "sink_name=%s", + avahi_address_snprint(at, sizeof(at), a), port, + vname);*/ + if (nicename) { + args = pa_sprintf_malloc("server=%s " + "sink_name=%s " + "description=\"%s\"", + avahi_address_snprint(at, sizeof(at), a), + vname, + nicename); + + } else { + args = pa_sprintf_malloc("server=%s " + "sink_name=%s", + avahi_address_snprint(at, sizeof(at), a), + vname); + } + + pa_log_debug("Loading module-raop-sink with arguments '%s'", args); + + if ((m = pa_module_load(u->core, "module-raop-sink", args))) { + tnl->module_index = m->index; + pa_hashmap_put(u->tunnels, tnl, tnl); + tnl = NULL; + } + + pa_xfree(vname); + pa_xfree(args); + avahi_free(device); + } + +finish: + + avahi_service_resolver_free(r); + + if (tnl) + tunnel_free(tnl); +} + +static void browser_cb( + AvahiServiceBrowser *b, + AvahiIfIndex interface, AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, const char *type, const char *domain, + AvahiLookupResultFlags flags, + void *userdata) { + + struct userdata *u = userdata; + struct tunnel *t; + + pa_assert(u); + + if (flags & AVAHI_LOOKUP_RESULT_LOCAL) + return; + + t = tunnel_new(interface, protocol, name, type, domain); + + if (event == AVAHI_BROWSER_NEW) { + + if (!pa_hashmap_get(u->tunnels, t)) + if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u))) + pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client))); + + /* We ignore the returned resolver object here, since the we don't + * need to attach any special data to it, and we can still destroy + * it from the callback */ + + } else if (event == AVAHI_BROWSER_REMOVE) { + struct tunnel *t2; + + if ((t2 = pa_hashmap_get(u->tunnels, t))) { + pa_module_unload_by_index(u->core, t2->module_index, TRUE); + pa_hashmap_remove(u->tunnels, t2); + tunnel_free(t2); + } + } + + tunnel_free(t); +} + +static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) { + struct userdata *u = userdata; + + pa_assert(c); + pa_assert(u); + + u->client = c; + + switch (state) { + case AVAHI_CLIENT_S_REGISTERING: + case AVAHI_CLIENT_S_RUNNING: + case AVAHI_CLIENT_S_COLLISION: + + if (!u->sink_browser) { + + if (!(u->sink_browser = avahi_service_browser_new( + c, + AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + SERVICE_TYPE_SINK, + NULL, + 0, + browser_cb, u))) { + + pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c))); + pa_module_unload_request(u->module, TRUE); + } + } + + break; + + case AVAHI_CLIENT_FAILURE: + if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) { + int error; + + pa_log_debug("Avahi daemon disconnected."); + + if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) { + pa_log("avahi_client_new() failed: %s", avahi_strerror(error)); + pa_module_unload_request(u->module, TRUE); + } + } + + /* Fall through */ + + case AVAHI_CLIENT_CONNECTING: + + if (u->sink_browser) { + avahi_service_browser_free(u->sink_browser); + u->sink_browser = NULL; + } + + break; + + default: ; + } +} + +int pa__init(pa_module*m) { + + struct userdata *u; + pa_modargs *ma = NULL; + int error; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + u->core = m->core; + u->module = m; + u->sink_browser = NULL; + + u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare); + + u->avahi_poll = pa_avahi_poll_new(m->core->mainloop); + + if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) { + pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error)); + goto fail; + } + + pa_modargs_free(ma); + + return 0; + +fail: + pa__done(m); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa__done(pa_module*m) { + struct userdata*u; + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->client) + avahi_client_free(u->client); + + if (u->avahi_poll) + pa_avahi_poll_free(u->avahi_poll); + + if (u->tunnels) { + struct tunnel *t; + + while ((t = pa_hashmap_steal_first(u->tunnels))) { + pa_module_unload_by_index(u->core, t->module_index, TRUE); + tunnel_free(t); + } + + pa_hashmap_free(u->tunnels, NULL, NULL); + } + + pa_xfree(u); +} diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c new file mode 100644 index 00000000..54de42c2 --- /dev/null +++ b/src/modules/raop/module-raop-sink.c @@ -0,0 +1,697 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2008 Colin Guthrie + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LINUX_SOCKIOS_H +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-raop-sink-symdef.h" +#include "rtp.h" +#include "sdp.h" +#include "sap.h" +#include "raop_client.h" + +PA_MODULE_AUTHOR("Colin Guthrie"); +PA_MODULE_DESCRIPTION("RAOP Sink"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE( + "sink_name= " + "sink_properties= " + "server=
" + "format= " + "rate= " + "channels="); + +#define DEFAULT_SINK_NAME "raop" + +struct userdata { + pa_core *core; + pa_module *module; + pa_sink *sink; + + pa_thread_mq thread_mq; + pa_rtpoll *rtpoll; + pa_rtpoll_item *rtpoll_item; + pa_thread *thread; + + pa_memchunk raw_memchunk; + pa_memchunk encoded_memchunk; + + void *write_data; + size_t write_length, write_index; + + void *read_data; + size_t read_length, read_index; + + pa_usec_t latency; + + /*esd_format_t format;*/ + int32_t rate; + + pa_smoother *smoother; + int fd; + + int64_t offset; + int64_t encoding_overhead; + int32_t next_encoding_overhead; + double encoding_ratio; + + pa_raop_client *raop; + + size_t block_size; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "sink_properties", + "server", + "format", + "rate", + "channels", + "description", /* supported for compatibility reasons, made redundant by sink_properties= */ + NULL +}; + +enum { + SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX, + SINK_MESSAGE_RIP_SOCKET +}; + +/* Forward declaration */ +static void sink_set_volume_cb(pa_sink *); + +static void on_connection(int fd, void*userdata) { + int so_sndbuf = 0; + socklen_t sl = sizeof(int); + struct userdata *u = userdata; + pa_assert(u); + + pa_assert(u->fd < 0); + u->fd = fd; + + if (getsockopt(u->fd, SOL_SOCKET, SO_SNDBUF, &so_sndbuf, &sl) < 0) + pa_log_warn("getsockopt(SO_SNDBUF) failed: %s", pa_cstrerror(errno)); + else { + pa_log_debug("SO_SNDBUF is %zu.", (size_t) so_sndbuf); + pa_sink_set_max_request(u->sink, PA_MAX((size_t) so_sndbuf, u->block_size)); + } + + /* Set the initial volume */ + sink_set_volume_cb(u->sink); + + pa_log_debug("Connection authenticated, handing fd to IO thread..."); + + pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL); +} + +static void on_close(void*userdata) { + struct userdata *u = userdata; + pa_assert(u); + + pa_log_debug("Connection closed, informing IO thread..."); + + pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RIP_SOCKET, NULL, 0, NULL, NULL); +} + +static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { + struct userdata *u = PA_SINK(o)->userdata; + + switch (code) { + + case PA_SINK_MESSAGE_SET_STATE: + + switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { + + case PA_SINK_SUSPENDED: + pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); + + pa_smoother_pause(u->smoother, pa_rtclock_usec()); + + /* Issue a FLUSH if we are connected */ + if (u->fd >= 0) { + pa_raop_flush(u->raop); + } + break; + + case PA_SINK_IDLE: + case PA_SINK_RUNNING: + + if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { + pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE); + + /* The connection can be closed when idle, so check to + see if we need to reestablish it */ + if (u->fd < 0) + pa_raop_connect(u->raop); + else + pa_raop_flush(u->raop); + } + + break; + + case PA_SINK_UNLINKED: + case PA_SINK_INIT: + case PA_SINK_INVALID_STATE: + ; + } + + break; + + case PA_SINK_MESSAGE_GET_LATENCY: { + pa_usec_t w, r; + + r = pa_smoother_get(u->smoother, pa_rtclock_usec()); + w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec); + + *((pa_usec_t*) data) = w > r ? w - r : 0; + return 0; + } + + case SINK_MESSAGE_PASS_SOCKET: { + struct pollfd *pollfd; + + pa_assert(!u->rtpoll_item); + + u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + pollfd->fd = u->fd; + pollfd->events = POLLOUT; + /*pollfd->events = */pollfd->revents = 0; + + if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { + /* Our stream has been suspended so we just flush it.... */ + pa_raop_flush(u->raop); + } + return 0; + } + + case SINK_MESSAGE_RIP_SOCKET: { + pa_assert(u->fd >= 0); + + pa_close(u->fd); + u->fd = -1; + + if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { + + pa_log_debug("RTSP control connection closed, but we're suspended so let's not worry about it... we'll open it again later"); + + if (u->rtpoll_item) + pa_rtpoll_item_free(u->rtpoll_item); + u->rtpoll_item = NULL; + } else { + /* Quesiton: is this valid here: or should we do some sort of: + return pa_sink_process_msg(PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL); + ?? */ + pa_module_unload_request(u->module, TRUE); + } + return 0; + } + } + + return pa_sink_process_msg(o, code, data, offset, chunk); +} + +static void sink_set_volume_cb(pa_sink *s) { + struct userdata *u = s->userdata; + pa_cvolume hw; + pa_volume_t v; + char t[PA_CVOLUME_SNPRINT_MAX]; + + pa_assert(u); + + /* If we're muted we don't need to do anything */ + if (s->muted) + return; + + /* Calculate the max volume of all channels. + We'll use this as our (single) volume on the APEX device and emulate + any variation in channel volumes in software */ + v = pa_cvolume_max(&s->virtual_volume); + + /* Create a pa_cvolume version of our single value */ + pa_cvolume_set(&hw, s->sample_spec.channels, v); + + /* Perform any software manipulation of the volume needed */ + pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &hw); + + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); + pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &hw)); + pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); + + /* Any necessary software volume manipulateion is done so set + our hw volume (or v as a single value) on the device */ + pa_raop_client_set_volume(u->raop, v); +} + +static void sink_set_mute_cb(pa_sink *s) { + struct userdata *u = s->userdata; + + pa_assert(u); + + if (s->muted) { + pa_raop_client_set_volume(u->raop, PA_VOLUME_MUTED); + } else { + sink_set_volume_cb(s); + } +} + +static void thread_func(void *userdata) { + struct userdata *u = userdata; + int write_type = 0; + pa_memchunk silence; + uint32_t silence_overhead = 0; + double silence_ratio = 0; + + pa_assert(u); + + pa_log_debug("Thread starting up"); + + pa_thread_mq_install(&u->thread_mq); + pa_rtpoll_install(u->rtpoll); + + pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); + + /* Create a chunk of memory that is our encoded silence sample. */ + pa_memchunk_reset(&silence); + + for (;;) { + int ret; + + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) + if (u->sink->thread_info.rewind_requested) + pa_sink_process_rewind(u->sink, 0); + + if (u->rtpoll_item) { + struct pollfd *pollfd; + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + + /* Render some data and write it to the fifo */ + if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) { + pa_usec_t usec; + int64_t n; + void *p; + + if (!silence.memblock) { + pa_memchunk silence_tmp; + + pa_memchunk_reset(&silence_tmp); + silence_tmp.memblock = pa_memblock_new(u->core->mempool, 4096); + silence_tmp.length = 4096; + p = pa_memblock_acquire(silence_tmp.memblock); + memset(p, 0, 4096); + pa_memblock_release(silence_tmp.memblock); + pa_raop_client_encode_sample(u->raop, &silence_tmp, &silence); + pa_assert(0 == silence_tmp.length); + silence_overhead = silence_tmp.length - 4096; + silence_ratio = silence_tmp.length / 4096; + pa_memblock_unref(silence_tmp.memblock); + } + + for (;;) { + ssize_t l; + + if (u->encoded_memchunk.length <= 0) { + if (u->encoded_memchunk.memblock) + pa_memblock_unref(u->encoded_memchunk.memblock); + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { + size_t rl; + + /* We render real data */ + if (u->raw_memchunk.length <= 0) { + if (u->raw_memchunk.memblock) + pa_memblock_unref(u->raw_memchunk.memblock); + pa_memchunk_reset(&u->raw_memchunk); + + /* Grab unencoded data */ + pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); + } + pa_assert(u->raw_memchunk.length > 0); + + /* Encode it */ + rl = u->raw_memchunk.length; + u->encoding_overhead += u->next_encoding_overhead; + pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk); + u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); + u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); + } else { + /* We render some silence into our memchunk */ + memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk)); + pa_memblock_ref(silence.memblock); + + /* Calculate/store some values to be used with the smoother */ + u->next_encoding_overhead = silence_overhead; + u->encoding_ratio = silence_ratio; + } + } + pa_assert(u->encoded_memchunk.length > 0); + + p = pa_memblock_acquire(u->encoded_memchunk.memblock); + l = pa_write(u->fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type); + pa_memblock_release(u->encoded_memchunk.memblock); + + pa_assert(l != 0); + + if (l < 0) { + + if (errno == EINTR) + continue; + else if (errno == EAGAIN) { + + /* OK, we filled all socket buffers up + * now. */ + goto filled_up; + + } else { + pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); + goto fail; + } + + } else { + u->offset += l; + + u->encoded_memchunk.index += l; + u->encoded_memchunk.length -= l; + + pollfd->revents = 0; + + if (u->encoded_memchunk.length > 0) { + /* we've completely written the encoded data, so update our overhead */ + u->encoding_overhead += u->next_encoding_overhead; + + /* OK, we wrote less that we asked for, + * hence we can assume that the socket + * buffers are full now */ + goto filled_up; + } + } + } + + filled_up: + + /* At this spot we know that the socket buffers are + * fully filled up. This is the best time to estimate + * the playback position of the server */ + + n = u->offset - u->encoding_overhead; + +#ifdef SIOCOUTQ + { + int l; + if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0) + n -= (l / u->encoding_ratio); + } +#endif + + usec = pa_bytes_to_usec(n, &u->sink->sample_spec); + + if (usec > u->latency) + usec -= u->latency; + else + usec = 0; + + pa_smoother_put(u->smoother, pa_rtclock_usec(), usec); + } + + /* Hmm, nothing to do. Let's sleep */ + pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;*/ + } + + if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) + goto fail; + + if (ret == 0) + goto finish; + + if (u->rtpoll_item) { + struct pollfd* pollfd; + + pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + + if (pollfd->revents & ~POLLOUT) { + if (u->sink->thread_info.state != PA_SINK_SUSPENDED) { + pa_log("FIFO shutdown."); + goto fail; + } + + /* We expect this to happen on occasion if we are not sending data. + It's perfectly natural and normal and natural */ + if (u->rtpoll_item) + pa_rtpoll_item_free(u->rtpoll_item); + u->rtpoll_item = NULL; + } + } + } + +fail: + /* If this was no regular exit from the loop we have to continue + * processing messages until we received PA_MESSAGE_SHUTDOWN */ + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); + pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: + if (silence.memblock) + pa_memblock_unref(silence.memblock); + pa_log_debug("Thread shutting down"); +} + +int pa__init(pa_module*m) { + struct userdata *u = NULL; + pa_sample_spec ss; + pa_modargs *ma = NULL; + const char *server, *desc; + pa_sink_new_data data; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("failed to parse module arguments"); + goto fail; + } + + ss = m->core->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log("invalid sample format specification"); + goto fail; + } + + if ((/*ss.format != PA_SAMPLE_U8 &&*/ ss.format != PA_SAMPLE_S16NE) || + (ss.channels > 2)) { + pa_log("sample type support is limited to mono/stereo and U8 or S16NE sample data"); + goto fail; + } + + u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + m->userdata = u; + u->fd = -1; + u->smoother = pa_smoother_new( + PA_USEC_PER_SEC, + PA_USEC_PER_SEC*2, + TRUE, + TRUE, + 10, + 0, + FALSE); + pa_memchunk_reset(&u->raw_memchunk); + pa_memchunk_reset(&u->encoded_memchunk); + u->offset = 0; + u->encoding_overhead = 0; + u->next_encoding_overhead = 0; + u->encoding_ratio = 1.0; + + u->rtpoll = pa_rtpoll_new(); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); + u->rtpoll_item = NULL; + + /*u->format = + (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) | + (ss.channels == 2 ? ESD_STEREO : ESD_MONO);*/ + u->rate = ss.rate; + u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss); + + u->read_data = u->write_data = NULL; + u->read_index = u->write_index = u->read_length = u->write_length = 0; + + /*u->state = STATE_AUTH;*/ + u->latency = 0; + + if (!(server = pa_modargs_get_value(ma, "server", NULL))) { + pa_log("No server argument given."); + goto fail; + } + + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "music"); + if ((desc = pa_modargs_get_value(ma, "description", NULL))) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, desc); + else + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "RAOP sink '%s'", server); + + if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { + pa_log("Invalid properties"); + pa_sink_new_data_done(&data); + goto fail; + } + + u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK); + pa_sink_new_data_done(&data); + + if (!u->sink) { + pa_log("Failed to create sink."); + goto fail; + } + + u->sink->parent.process_msg = sink_process_msg; + u->sink->userdata = u; + u->sink->set_volume = sink_set_volume_cb; + u->sink->set_mute = sink_set_mute_cb; + u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL; + + pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); + pa_sink_set_rtpoll(u->sink, u->rtpoll); + + if (!(u->raop = pa_raop_client_new(u->core, server))) { + pa_log("Failed to connect to server."); + goto fail; + } + + pa_raop_client_set_callback(u->raop, on_connection, u); + pa_raop_client_set_closed_callback(u->raop, on_close, u); + + if (!(u->thread = pa_thread_new(thread_func, u))) { + pa_log("Failed to create thread."); + goto fail; + } + + pa_sink_put(u->sink); + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; +} + +int pa__get_n_used(pa_module *m) { + struct userdata *u; + + pa_assert(m); + pa_assert_se(u = m->userdata); + + return pa_sink_linked_by(u->sink); +} + +void pa__done(pa_module*m) { + struct userdata *u; + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->sink) + pa_sink_unlink(u->sink); + + if (u->thread) { + pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); + pa_thread_free(u->thread); + } + + pa_thread_mq_done(&u->thread_mq); + + if (u->sink) + pa_sink_unref(u->sink); + + if (u->rtpoll_item) + pa_rtpoll_item_free(u->rtpoll_item); + + if (u->rtpoll) + pa_rtpoll_free(u->rtpoll); + + if (u->raw_memchunk.memblock) + pa_memblock_unref(u->raw_memchunk.memblock); + + if (u->encoded_memchunk.memblock) + pa_memblock_unref(u->encoded_memchunk.memblock); + + if (u->raop) + pa_raop_client_free(u->raop); + + pa_xfree(u->read_data); + pa_xfree(u->write_data); + + if (u->smoother) + pa_smoother_free(u->smoother); + + if (u->fd >= 0) + pa_close(u->fd); + + pa_xfree(u); +} -- cgit From 46b8ca21d1ef56df298cfa9412e73fdf17cbea49 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 23:17:37 +0200 Subject: native-protocol: allow enumerating ports --- src/pulse/introspect.c | 110 +++++++++++++++++++++++++++++++++++----- src/pulse/introspect.h | 24 +++++++++ src/pulsecore/protocol-native.c | 35 +++++++++++++ 3 files changed, 156 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 0c45f99f..ab67f596 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -49,7 +49,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, uint32_t t pa_assert(o); pa_assert(PA_REFCNT_VALUE(o) >= 1); - memset(&i, 0, sizeof(i)); + pa_zero(i); if (!o->context) goto finish; @@ -93,7 +93,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, pa_assert(o); pa_assert(PA_REFCNT_VALUE(o) >= 1); - memset(&i, 0, sizeof(i)); + pa_zero(i); if (!o->context) goto finish; @@ -161,8 +161,10 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u pa_bool_t mute; uint32_t flags; uint32_t state; + uint32_t j; + const char *ap = NULL; - memset(&i, 0, sizeof(i)); + pa_zero(i); i.proplist = pa_proplist_new(); i.base_volume = PA_VOLUME_NORM; i.n_volume_steps = PA_VOLUME_NORM+1; @@ -190,13 +192,53 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u (pa_tagstruct_get_volume(t, &i.base_volume) < 0 || pa_tagstruct_getu32(t, &state) < 0 || pa_tagstruct_getu32(t, &i.n_volume_steps) < 0 || - pa_tagstruct_getu32(t, &i.card) < 0))) { + pa_tagstruct_getu32(t, &i.card) < 0)) || + (o->context->version >= 16 && + (pa_tagstruct_getu32(t, &i.n_ports)))) { pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_proplist_free(i.proplist); goto finish; } + if (i.n_ports > 0) { + i.ports = pa_xnew(pa_sink_port_info*, i.n_ports+1); + i.ports[0] = pa_xnew(pa_sink_port_info, i.n_ports); + + for (j = 0; j < i.n_ports; j++) { + if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 || + pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 || + pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.ports); + pa_xfree(i.ports[0]); + pa_proplist_free(i.proplist); + goto finish; + } + + i.ports[j] = &i.ports[0][j]; + } + + i.ports[j] = NULL; + } + + if (pa_tagstruct_gets(t, &ap) < 0) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.ports[0]); + pa_xfree(i.ports); + pa_proplist_free(i.proplist); + goto finish; + } + + if (ap) { + for (j = 0; j < i.n_ports; j++) + if (pa_streq(i.ports[j]->name, ap)) { + i.active_port = i.ports[j]; + break; + } + } + i.mute = (int) mute; i.flags = (pa_sink_flags_t) flags; i.state = (pa_sink_state_t) state; @@ -346,8 +388,10 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, pa_bool_t mute; uint32_t flags; uint32_t state; + unsigned j; + const char *ap; - memset(&i, 0, sizeof(i)); + pa_zero(i); i.proplist = pa_proplist_new(); i.base_volume = PA_VOLUME_NORM; i.n_volume_steps = PA_VOLUME_NORM+1; @@ -375,13 +419,53 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, (pa_tagstruct_get_volume(t, &i.base_volume) < 0 || pa_tagstruct_getu32(t, &state) < 0 || pa_tagstruct_getu32(t, &i.n_volume_steps) < 0 || - pa_tagstruct_getu32(t, &i.card) < 0))) { + pa_tagstruct_getu32(t, &i.card) < 0)) || + (o->context->version >= 16 && + (pa_tagstruct_getu32(t, &i.n_ports)))) { pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_proplist_free(i.proplist); goto finish; } + if (i.n_ports > 0) { + i.ports = pa_xnew(pa_source_port_info*, i.n_ports+1); + i.ports[0] = pa_xnew(pa_source_port_info, i.n_ports); + + for (j = 0; j < i.n_ports; j++) { + if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 || + pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 || + pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.ports[0]); + pa_xfree(i.ports); + pa_proplist_free(i.proplist); + goto finish; + } + + i.ports[j] = &i.ports[0][j]; + } + + i.ports[j] = NULL; + } + + if (pa_tagstruct_gets(t, &ap) < 0) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.ports[0]); + pa_xfree(i.ports); + pa_proplist_free(i.proplist); + goto finish; + } + + if (ap) { + for (j = 0; j < i.n_ports; j++) + if (pa_streq(i.ports[j]->name, ap)) { + i.active_port = i.ports[j]; + break; + } + } + i.mute = (int) mute; i.flags = (pa_source_flags_t) flags; i.state = (pa_source_state_t) state; @@ -529,7 +613,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, while (!pa_tagstruct_eof(t)) { pa_client_info i; - memset(&i, 0, sizeof(i)); + pa_zero(i); i.proplist = pa_proplist_new(); if (pa_tagstruct_getu32(t, &i.index) < 0 || @@ -614,7 +698,7 @@ static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, u uint32_t j; const char*ap; - memset(&i, 0, sizeof(i)); + pa_zero(i); if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || @@ -627,7 +711,7 @@ static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, u } if (i.n_profiles > 0) { - i.profiles = pa_xnew(pa_card_profile_info, i.n_profiles+1); + i.profiles = pa_xnew0(pa_card_profile_info, i.n_profiles+1); for (j = 0; j < i.n_profiles; j++) { @@ -815,7 +899,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, pa_module_info i; pa_bool_t auto_unload = FALSE; - memset(&i, 0, sizeof(i)); + pa_zero(i); i.proplist = pa_proplist_new(); if (pa_tagstruct_getu32(t, &i.index) < 0 || @@ -900,7 +984,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm pa_sink_input_info i; pa_bool_t mute = FALSE; - memset(&i, 0, sizeof(i)); + pa_zero(i); i.proplist = pa_proplist_new(); if (pa_tagstruct_getu32(t, &i.index) < 0 || @@ -994,7 +1078,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c while (!pa_tagstruct_eof(t)) { pa_source_output_info i; - memset(&i, 0, sizeof(i)); + pa_zero(i); i.proplist = pa_proplist_new(); if (pa_tagstruct_getu32(t, &i.index) < 0 || @@ -1336,7 +1420,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, pa_sample_info i; pa_bool_t lazy = FALSE; - memset(&i, 0, sizeof(i)); + pa_zero(i); i.proplist = pa_proplist_new(); if (pa_tagstruct_getu32(t, &i.index) < 0 || diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 87824239..ee982100 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -193,6 +193,15 @@ PA_C_DECL_BEGIN /** @{ \name Sinks */ +/** Stores information about a specific port of a sink. Please + * note that this structure can be extended as part of evolutionary + * API updates at any time in any new release. \since 0.9.16 */ +typedef struct pa_sink_port_info { + const char *name; /**< Name of this port */ + const char *description; /**< Description of this port */ + uint32_t priority; /**< The higher this value is the more useful this port is as a default */ +} pa_sink_port_info; + /** Stores information about sinks. Please note that this structure * can be extended as part of evolutionary API updates at any time in * any new release. */ @@ -216,6 +225,9 @@ typedef struct pa_sink_info { pa_sink_state_t state; /**< State \since 0.9.15 */ uint32_t n_volume_steps; /**< Number of volume steps for sinks which do not support arbitrary volumes. \since 0.9.15 */ uint32_t card; /**< Card index, or PA_INVALID_INDEX. \since 0.9.15 */ + uint32_t n_ports; /**< Number of entries in port array \since 0.9.16 */ + pa_sink_port_info** ports; /**< Array of available ports, or NULL. Array is terminated by an entry set to NULL. The number of entries is stored in n_ports \since 0.9.16 */ + pa_sink_port_info* active_port; /**< Pointer to active port in the array, or NULL \since 0.9.16 */ } pa_sink_info; /** Callback prototype for pa_context_get_sink_info_by_name() and friends */ @@ -258,6 +270,15 @@ pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char*name, c /** @{ \name Sources */ +/** Stores information about a specific port of a source. Please + * note that this structure can be extended as part of evolutionary + * API updates at any time in any new release. \since 0.9.16 */ +typedef struct pa_source_port_info { + const char *name; /**< Name of this port */ + const char *description; /**< Description of this port */ + uint32_t priority; /**< The higher this value is the more useful this port is as a default */ +} pa_source_port_info; + /** Stores information about sources. Please note that this structure * can be extended as part of evolutionary API updates at any time in * any new release. */ @@ -281,6 +302,9 @@ typedef struct pa_source_info { pa_source_state_t state; /**< State \since 0.9.15 */ uint32_t n_volume_steps; /**< Number of volume steps for sources which do not support arbitrary volumes. \since 0.9.15 */ uint32_t card; /**< Card index, or PA_INVALID_INDEX. \since 0.9.15 */ + uint32_t n_ports; /**< Number of entries in port array \since 0.9.16 */ + pa_source_port_info** ports; /**< Array of available ports, or NULL. Array is terminated by an entry set to NULL. The number of entries is stored in n_ports \since 0.9.16 */ + pa_source_port_info* active_port; /**< Pointer to active port in the array, or NULL \since 0.9.16 */ } pa_source_info; /** Callback prototype for pa_context_get_source_info_by_name() and friends */ diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 92efc9ee..48f7b135 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -2845,6 +2845,23 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin pa_tagstruct_putu32(t, sink->n_volume_steps); pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX); } + + if (c->version >= 16) { + pa_tagstruct_putu32(t, sink->ports ? pa_hashmap_size(sink->ports) : 0); + + if (sink->ports) { + void *state; + pa_device_port *p; + + PA_HASHMAP_FOREACH(p, sink->ports, state) { + pa_tagstruct_puts(t, p->name); + pa_tagstruct_puts(t, p->description); + pa_tagstruct_putu32(t, p->priority); + } + } + + pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL); + } } static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) { @@ -2885,6 +2902,24 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s pa_tagstruct_putu32(t, source->n_volume_steps); pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX); } + + if (c->version >= 16) { + + pa_tagstruct_putu32(t, source->ports ? pa_hashmap_size(source->ports) : 0); + + if (source->ports) { + void *state; + pa_device_port *p; + + PA_HASHMAP_FOREACH(p, source->ports, state) { + pa_tagstruct_puts(t, p->name); + pa_tagstruct_puts(t, p->description); + pa_tagstruct_putu32(t, p->priority); + } + } + + pa_tagstruct_puts(t, source->active_port ? source->active_port->name : NULL); + } } static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) { -- cgit From 75256fb671b6ae8d784e0d6415d292fdbc6482c2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Jun 2009 23:40:46 +0200 Subject: pactl: show list of supported ports --- src/utils/pactl.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src') diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 1ae15c75..c8c3a437 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -254,6 +254,18 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_ pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); pa_xfree(pl); + + if (i->ports) { + pa_sink_port_info **p; + + printf(_("\tPorts:\n")); + for (p = i->ports; *p; p++) + printf("\t\t%s: %s (priority. %u)\n", (*p)->name, (*p)->description, (*p)->priority); + } + + if (i->active_port) + printf(_("\tActive Port: %s\n"), + i->active_port->name); } static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) { @@ -334,6 +346,18 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); pa_xfree(pl); + + if (i->ports) { + pa_source_port_info **p; + + printf(_("\tPorts:\n")); + for (p = i->ports; *p; p++) + printf("\t\t%s: %s (priority. %u)\n", (*p)->name, (*p)->description, (*p)->priority); + } + + if (i->active_port) + printf(_("\tActive Port: %s\n"), + i->active_port->name); } static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) { -- cgit From d773638b20b0a6750a5405521ee0c8974d1b72ec Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 00:56:46 +0200 Subject: stream-restore: e don't need to save stream data that came from the database --- src/modules/module-stream-restore.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 2de98f4e..41206c79 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -370,7 +370,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n if (!new_data->sink) { pa_log_info("Restoring device for stream %s.", name); new_data->sink = s; - new_data->save_sink = TRUE; + new_data->save_sink = FALSE; } else pa_log_info("Not restore device for stream %s, because already set.", name); } @@ -419,7 +419,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu if (!new_data->muted_is_set) { pa_log_info("Restoring mute state for sink input %s.", name); pa_sink_input_new_data_set_muted(new_data, e->muted); - new_data->save_muted = TRUE; + new_data->save_muted = FALSE; } else pa_log_debug("Not restoring mute state for sink input %s, because already set.", name); } @@ -455,7 +455,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou if (!new_data->source) { pa_log_info("Restoring device for stream %s.", name); new_data->source = s; - new_data->save_source = TRUE; + new_data->save_source = FALSE; } else pa_log_info("Not restoring device for stream %s, because already set", name); } @@ -503,7 +503,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { if (u->restore_muted && e->muted_valid) { pa_log_info("Restoring mute state for sink input %s.", name); - pa_sink_input_set_mute(si, e->muted, TRUE); + pa_sink_input_set_mute(si, e->muted, FALSE); } if (u->restore_device && @@ -511,7 +511,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) { pa_log_info("Restoring device for stream %s.", name); - pa_sink_input_move_to(si, s, TRUE); + pa_sink_input_move_to(si, s, FALSE); } } @@ -533,7 +533,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) { pa_log_info("Restoring device for stream %s.", name); - pa_source_output_move_to(so, s, TRUE); + pa_source_output_move_to(so, s, FALSE); } } } -- cgit From 59bd793734d71c6baa5ed35044f18165ffe9685c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 00:58:19 +0200 Subject: card-restore: we don't need to save card data that came from the database --- src/modules/module-card-restore.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c index 85478d12..62c50616 100644 --- a/src/modules/module-card-restore.c +++ b/src/modules/module-card-restore.c @@ -197,8 +197,9 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new if ((e = read_entry(u, new_data->name)) && e->profile[0]) { if (!new_data->active_profile) { - pa_card_new_data_set_profile(new_data, e->profile); pa_log_info("Restoring profile for card %s.", new_data->name); + pa_card_new_data_set_profile(new_data, e->profile); + new_data->save_profile = FALSE; } else pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name); -- cgit From 0e4ac56afc5b317c01d4aa29ff4c0842976d41ac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 00:59:04 +0200 Subject: device-restore: save last used port in database --- src/modules/module-device-restore.c | 202 ++++++++++++++++++++++++++++++------ 1 file changed, 171 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index ae21acd5..5fa6f061 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -54,6 +54,7 @@ PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); PA_MODULE_USAGE( + "restore_port= " "restore_volume= " "restore_muted="); @@ -62,6 +63,7 @@ PA_MODULE_USAGE( static const char* const valid_modargs[] = { "restore_volume", "restore_muted", + "restore_port", NULL }; @@ -70,22 +72,27 @@ struct userdata { pa_module *module; pa_subscription *subscription; pa_hook_slot + *sink_new_hook_slot, *sink_fixate_hook_slot, + *source_new_hook_slot, *source_fixate_hook_slot; pa_time_event *save_time_event; pa_database *database; pa_bool_t restore_volume:1; pa_bool_t restore_muted:1; + pa_bool_t restore_port:1; }; -#define ENTRY_VERSION 1 +#define ENTRY_VERSION 2 struct entry { uint8_t version; + pa_bool_t muted_valid:1, volume_valid:1, port_valid:1; pa_bool_t muted:1; pa_channel_map channel_map; pa_cvolume volume; + char port[PA_NAME_MAX]; } PA_GCC_PACKED; static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { @@ -131,17 +138,17 @@ static struct entry* read_entry(struct userdata *u, const char *name) { goto fail; } - if (!(pa_cvolume_valid(&e->volume))) { - pa_log_warn("Invalid volume stored in database for device %s", name); + if (!memchr(e->port, 0, sizeof(e->port))) { + pa_log_warn("Database contains entry for device %s with missing NUL byte in port name", name); goto fail; } - if (!(pa_channel_map_valid(&e->channel_map))) { + if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) { pa_log_warn("Invalid channel map stored in database for device %s", name); goto fail; } - if (e->volume.channels != e->channel_map.channels) { + if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) { pa_log_warn("Volume and channel map don't match in database entry for device %s", name); goto fail; } @@ -165,6 +172,25 @@ static void trigger_save(struct userdata *u) { u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u); } +static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { + pa_cvolume t; + + if (a->port_valid != b->port_valid || + (a->port_valid && strncmp(a->port, b->port, sizeof(a->port)))) + return FALSE; + + if (a->muted_valid != b->muted_valid || + (a->muted_valid && (a->muted != b->muted))) + return FALSE; + + t = b->volume; + if (a->volume_valid != b->volume_valid || + (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume))) + return FALSE; + + return TRUE; +} + static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { struct userdata *u = userdata; struct entry entry, *old; @@ -190,9 +216,25 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 return; name = pa_sprintf_malloc("sink:%s", sink->name); - entry.channel_map = sink->channel_map; - entry.volume = *pa_sink_get_volume(sink, FALSE, TRUE); - entry.muted = pa_sink_get_mute(sink, FALSE); + + if ((old = read_entry(u, name))) + entry = *old; + + if (sink->save_volume) { + entry.channel_map = sink->channel_map; + entry.volume = *pa_sink_get_volume(sink, FALSE, TRUE); + entry.volume_valid = TRUE; + } + + if (sink->save_volume) { + entry.muted = pa_sink_get_mute(sink, FALSE); + entry.muted_valid = TRUE; + } + + if (sink->save_port) { + pa_strlcpy(entry.port, sink->active_port ? sink->active_port->name : "", sizeof(entry.port)); + entry.port_valid = TRUE; + } } else { pa_source *source; @@ -203,16 +245,30 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 return; name = pa_sprintf_malloc("source:%s", source->name); - entry.channel_map = source->channel_map; - entry.volume = *pa_source_get_volume(source, FALSE); - entry.muted = pa_source_get_mute(source, FALSE); - } - if ((old = read_entry(u, name))) { + if ((old = read_entry(u, name))) + entry = *old; - if (pa_cvolume_equal(pa_cvolume_remap(&old->volume, &old->channel_map, &entry.channel_map), &entry.volume) && - !old->muted == !entry.muted) { + if (source->save_volume) { + entry.channel_map = source->channel_map; + entry.volume = *pa_source_get_volume(source, FALSE); + entry.volume_valid = TRUE; + } + if (source->save_muted) { + entry.muted = pa_source_get_mute(source, FALSE); + entry.muted_valid = TRUE; + } + + if (source->save_port) { + pa_strlcpy(entry.port, source->active_port ? source->active_port->name : "", sizeof(entry.port)); + entry.port_valid = TRUE; + } + } + + if (old) { + + if (entries_equal(old, &entry)) { pa_xfree(old); pa_xfree(name); return; @@ -227,7 +283,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 data.data = &entry; data.size = sizeof(entry); - pa_log_info("Storing volume/mute for device %s.", name); + pa_log_info("Storing volume/mute/port for device %s.", name); pa_database_set(u->database, &key, &data, TRUE); @@ -236,6 +292,36 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 trigger_save(u); } +static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) { + char *name; + struct entry *e; + + pa_assert(new_data); + + if (!u->restore_port) + return PA_HOOK_OK; + + name = pa_sprintf_malloc("sink:%s", new_data->name); + + if ((e = read_entry(u, name))) { + + if (e->port_valid) { + if (!new_data->active_port) { + pa_log_info("Restoring port for sink %s.", name); + pa_sink_new_data_set_port(new_data, e->port); + new_data->save_port = FALSE; + } else + pa_log_debug("Not restoring port for sink %s, because already set.", name); + } + + pa_xfree(e); + } + + pa_xfree(name); + + return PA_HOOK_OK; +} + static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) { char *name; struct entry *e; @@ -246,21 +332,28 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data * if ((e = read_entry(u, name))) { - if (u->restore_volume) { + if (u->restore_volume && e->volume_valid) { if (!new_data->volume_is_set) { + pa_cvolume v; + pa_log_info("Restoring volume for sink %s.", new_data->name); - pa_sink_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map)); + + v = e->volume; + pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map); + pa_sink_new_data_set_volume(new_data, &v); + + new_data->save_volume = FALSE; } else pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name); - } - if (u->restore_muted) { + if (u->restore_muted && e->muted_valid) { if (!new_data->muted_is_set) { pa_log_info("Restoring mute state for sink %s.", new_data->name); pa_sink_new_data_set_muted(new_data, e->muted); + new_data->save_muted = FALSE; } else pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name); } @@ -273,6 +366,36 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data * return PA_HOOK_OK; } +static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) { + char *name; + struct entry *e; + + pa_assert(new_data); + + if (!u->restore_port) + return PA_HOOK_OK; + + name = pa_sprintf_malloc("source:%s", new_data->name); + + if ((e = read_entry(u, name))) { + + if (e->port_valid) { + if (!new_data->active_port) { + pa_log_info("Restoring port for source %s.", name); + pa_source_new_data_set_port(new_data, e->port); + new_data->save_port = FALSE; + } else + pa_log_debug("Not restoring port for source %s, because already set.", name); + } + + pa_xfree(e); + } + + pa_xfree(name); + + return PA_HOOK_OK; +} + static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) { char *name; struct entry *e; @@ -283,20 +406,28 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da if ((e = read_entry(u, name))) { - if (u->restore_volume) { + if (u->restore_volume && e->volume_valid) { if (!new_data->volume_is_set) { + pa_cvolume v; + pa_log_info("Restoring volume for source %s.", new_data->name); - pa_source_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map)); + + v = e->volume; + pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map); + pa_source_new_data_set_volume(new_data, &v); + + new_data->save_volume = FALSE; } else pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name); } - if (u->restore_muted) { + if (u->restore_muted && e->muted_valid) { if (!new_data->muted_is_set) { pa_log_info("Restoring mute state for source %s.", new_data->name); pa_source_new_data_set_muted(new_data, e->muted); + new_data->save_muted = FALSE; } else pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name); } @@ -316,7 +447,7 @@ int pa__init(pa_module*m) { pa_sink *sink; pa_source *source; uint32_t idx; - pa_bool_t restore_volume = TRUE, restore_muted = TRUE; + pa_bool_t restore_volume = TRUE, restore_muted = TRUE, restore_port = TRUE; pa_assert(m); @@ -326,24 +457,29 @@ int pa__init(pa_module*m) { } if (pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 || - pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0) { - pa_log("restore_volume= and restore_muted= expect boolean arguments"); + pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 || + pa_modargs_get_value_boolean(ma, "restore_port", &restore_port) < 0) { + pa_log("restore_port=, restore_volume= and restore_muted= expect boolean arguments"); goto fail; } - if (!restore_muted && !restore_volume) - pa_log_warn("Neither restoring volume nor restoring muted enabled!"); + if (!restore_muted && !restore_volume && !restore_port) + pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!"); - m->userdata = u = pa_xnew(struct userdata, 1); + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; - u->save_time_event = NULL; u->restore_volume = restore_volume; u->restore_muted = restore_muted; - u->database = NULL; + u->restore_port = restore_port; u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u); + if (restore_port) { + u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u); + u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u); + } + if (restore_muted || restore_volume) { u->sink_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_fixate_hook_callback, u); u->source_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_fixate_hook_callback, u); @@ -394,6 +530,10 @@ void pa__done(pa_module*m) { pa_hook_slot_free(u->sink_fixate_hook_slot); if (u->source_fixate_hook_slot) pa_hook_slot_free(u->source_fixate_hook_slot); + if (u->sink_new_hook_slot) + pa_hook_slot_free(u->sink_new_hook_slot); + if (u->source_new_hook_slot) + pa_hook_slot_free(u->source_new_hook_slot); if (u->save_time_event) u->core->mainloop->time_free(u->save_time_event); -- cgit From 4951e08031a74864b889ecdaaa3bcaa3a1f0ceb7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 00:59:33 +0200 Subject: card,stream-restore: minor cleanups --- src/modules/module-card-restore.c | 4 +--- src/modules/module-stream-restore.c | 14 +++++--------- 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c index 62c50616..85583b2c 100644 --- a/src/modules/module-card-restore.c +++ b/src/modules/module-card-restore.c @@ -223,11 +223,9 @@ int pa__init(pa_module*m) { goto fail; } - m->userdata = u = pa_xnew(struct userdata, 1); + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; - u->save_time_event = NULL; - u->database = NULL; u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_CARD, subscribe_callback, u); diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 41206c79..f2aea27b 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -362,9 +362,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n return PA_HOOK_OK; if ((e = read_entry(u, name))) { - pa_sink *s; if (e->device_valid) { + pa_sink *s; if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) { if (!new_data->sink) { @@ -372,7 +372,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n new_data->sink = s; new_data->save_sink = FALSE; } else - pa_log_info("Not restore device for stream %s, because already set.", name); + pa_log_info("Not restoring device for stream %s, because already set.", name); } } @@ -404,6 +404,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu pa_cvolume v; pa_log_info("Restoring volume for sink input %s.", name); + v = e->volume; pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map); pa_sink_input_new_data_set_volume(new_data, &v); @@ -793,14 +794,12 @@ int pa__init(pa_module*m) { if (!restore_muted && !restore_volume && !restore_device) pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring device enabled!"); - m->userdata = u = pa_xnew(struct userdata, 1); + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; - u->save_time_event = NULL; u->restore_device = restore_device; u->restore_volume = restore_volume; u->restore_muted = restore_muted; - u->database = NULL; u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); u->protocol = pa_native_protocol_get(m->core); @@ -818,10 +817,7 @@ int pa__init(pa_module*m) { if (restore_volume || restore_muted) u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u); - - fname = pa_state_path("stream-volumes", TRUE); - - if (!fname) + if (!(fname = pa_state_path("stream-volumes", TRUE))) goto fail; if (!(u->database = pa_database_open(fname, TRUE))) { -- cgit From 39b37a2bac34df671a3ff5a5d4ea8b4003f09fc9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 03:40:47 +0200 Subject: core: be a bit more verbose when registering a sink/source fails --- src/pulsecore/sink.c | 1 + src/pulsecore/source.c | 1 + 2 files changed, 2 insertions(+) (limited to 'src') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 47792293..7e4513e7 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -181,6 +181,7 @@ pa_sink* pa_sink_new( s = pa_msgobject_new(pa_sink); if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) { + pa_log_debug("Failed to register name %s.", data->name); pa_xfree(s); return NULL; } diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 1e431160..74f38bc5 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -149,6 +149,7 @@ pa_source* pa_source_new( s = pa_msgobject_new(pa_source); if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) { + pa_log_debug("Failed to register name %s.", data->name); pa_xfree(s); return NULL; } -- cgit From 8d3362cec6263301ec5a8b40cc0ef333ca721c30 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 03:41:49 +0200 Subject: device-restore: properly check save_muted flag when storing muted state --- src/modules/module-device-restore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index 5fa6f061..a2745b88 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -226,7 +226,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 entry.volume_valid = TRUE; } - if (sink->save_volume) { + if (sink->save_muted) { entry.muted = pa_sink_get_mute(sink, FALSE); entry.muted_valid = TRUE; } -- cgit From 34b48887ef74d7ec4d54282d4364ec2ee34e8234 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 03:42:39 +0200 Subject: alsa: when creating alsa sinks/sources include mapping name in device name to allow profiles mit multiple sinks or multiple sources --- src/modules/alsa/alsa-sink.c | 10 +++++++--- src/modules/alsa/alsa-source.c | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 2226bc6f..34ff6582 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1335,7 +1335,7 @@ finish: pa_log_debug("Thread shutting down"); } -static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name) { +static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name, pa_alsa_mapping *mapping) { const char *n; char *t; @@ -1356,7 +1356,11 @@ static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *de data->namereg_fail = FALSE; } - t = pa_sprintf_malloc("alsa_output.%s", n); + if (mapping) + t = pa_sprintf_malloc("alsa_output.%s.%s", n, mapping->name); + else + t = pa_sprintf_malloc("alsa_output.%s", n); + pa_sink_new_data_set_name(data, t); pa_xfree(t); } @@ -1679,7 +1683,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca data.driver = driver; data.module = m; data.card = card; - set_sink_name(&data, ma, dev_id, u->device_name); + set_sink_name(&data, ma, dev_id, u->device_name, mapping); pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_channel_map(&data, &map); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index f2e4e234..c8244c66 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1187,7 +1187,7 @@ finish: pa_log_debug("Thread shutting down"); } -static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name) { +static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name, pa_alsa_mapping *mapping) { const char *n; char *t; @@ -1208,7 +1208,11 @@ static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char data->namereg_fail = FALSE; } - t = pa_sprintf_malloc("alsa_input.%s", n); + if (mapping) + t = pa_sprintf_malloc("alsa_input.%s.%s", n, mapping->name); + else + t = pa_sprintf_malloc("alsa_input.%s", n); + pa_source_new_data_set_name(data, t); pa_xfree(t); } @@ -1528,7 +1532,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p data.driver = driver; data.module = m; data.card = card; - set_source_name(&data, ma, dev_id, u->device_name); + set_source_name(&data, ma, dev_id, u->device_name, mapping); pa_source_new_data_set_sample_spec(&data, &ss); pa_source_new_data_set_channel_map(&data, &map); -- cgit From b5cea8d5907a5af2072acfd4570fae3c99714502 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 03:43:22 +0200 Subject: alsa: fix duplicate mapping detection --- src/modules/alsa/alsa-mixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 6f21e103..c5db303e 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -2930,7 +2930,7 @@ static int profile_verify(pa_alsa_profile *p) { char **in; pa_bool_t duplicate = FALSE; - for (in = p->output_mapping_names; *in; in++) + for (in = name + 1; *in; in++) if (pa_streq(*name, *in)) { duplicate = TRUE; break; @@ -2963,7 +2963,7 @@ static int profile_verify(pa_alsa_profile *p) { char **in; pa_bool_t duplicate = FALSE; - for (in = p->input_mapping_names; *in; in++) + for (in = name + 1; *in; in++) if (pa_streq(*name, *in)) { duplicate = TRUE; break; -- cgit From 60df970bd9a17f4e7d2a09f5e20120be1beff2ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 03:43:51 +0200 Subject: alsa: properly count mappings of manually defined profiles --- src/modules/alsa/alsa-mixer.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index c5db303e..def26221 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -2945,6 +2945,9 @@ static int profile_verify(pa_alsa_profile *p) { } pa_idxset_put(p->output_mappings, m, NULL); + + if (p->supported) + m->supported++; } pa_xstrfreev(p->output_mapping_names); @@ -2978,6 +2981,9 @@ static int profile_verify(pa_alsa_profile *p) { } pa_idxset_put(p->input_mappings, m, NULL); + + if (p->supported) + m->supported++; } pa_xstrfreev(p->input_mapping_names); -- cgit From ca560ccc4a562c7a2a477331b72e7c62adf21a95 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 03:58:05 +0200 Subject: alsa: add profile set for Native Instruments Audio 8 DJ sound card --- src/Makefile.am | 9 +- .../alsa/mixer/profile-sets/78-pulseaudio.rules | 9 ++ .../profile-sets/native-instruments-audio8dj.conf | 160 +++++++++++++++++++++ 3 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules create mode 100644 src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 40b56757..f3099e26 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,6 +31,7 @@ pulselibexecdir=$(libexecdir)/pulse xdgautostartdir=$(sysconfdir)/xdg/autostart alsaprofilesetsdir=$(datadir)/alsa-mixer/profile-sets alsapathsdir=$(datadir)/alsa-mixer/paths +udevrulesdir=/lib/udev/rules.d ################################### # Defines # @@ -115,6 +116,8 @@ EXTRA_DIST = \ map-file \ daemon/org.pulseaudio.policy.in \ modules/alsa/mixer/profile-sets/default.conf \ + modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf \ + modules/alsa/mixer/profile-sets/78-pulseaudio.rules \ modules/alsa/mixer/paths/analog-input-aux.conf \ modules/alsa/mixer/paths/analog-input.conf \ modules/alsa/mixer/paths/analog-input.conf.common \ @@ -1045,7 +1048,11 @@ modlibexec_LTLIBRARIES += \ module-alsa-card.la alsaprofilesets_DATA = \ - modules/alsa/mixer/profile-sets/default.conf + modules/alsa/mixer/profile-sets/default.conf \ + modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf + +udevrules_DATA = \ + modules/alsa/mixer/profile-sets/78-pulseaudio.rules alsapaths_DATA = \ modules/alsa/mixer/paths/analog-input-aux.conf \ diff --git a/src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules b/src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules new file mode 100644 index 00000000..92a6b034 --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules @@ -0,0 +1,9 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM!="sound", GOTO="pulseaudio_end" +ACTION!="change", GOTO="pulseaudio_end" +KERNEL!="card*", GOTO="pulseaudio_end" + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1978", ENV{PULSE_PROFILE_SET}="native-instruments-audio8dj.conf" + +LABEL="pulseaudio_end" diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf new file mode 100644 index 00000000..fe1e9ae8 --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf @@ -0,0 +1,160 @@ +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Native Instruments Audio 8 DJ +; +; This card has four stereo pairs of input and four stereo pairs of +; output, named channels A to D. Channel C has an additional Mic/Line +; connector, channel D an additional Headphone connector. +; +; We knowingly only define a subset of the theoretically possible +; mapping combinations as profiles here. + +[General] +auto-profiles = no + +[Mapping analog-stereo-a] +description = Analog Stereo Channel A +device-strings = hw:%f,0,0 +channel-map = left,right + +[Mapping analog-stereo-b] +description = Analog Stereo Channel B +device-strings = hw:%f,0,1 +channel-map = left,right + +# Since we want to set a different description for channel C's/D's input +# and output we define two seperate mappings for them +[Mapping analog-stereo-c-output] +description = Analog Stereo Channel C +device-strings = hw:%f,0,2 +channel-map = left,right +direction = output + +[Mapping analog-stereo-c-input] +description = Analog Stereo Channel C (Line/Mic) +device-strings = hw:%f,0,2 +channel-map = left,right +direction = input + +[Mapping analog-stereo-d-output] +description = Analog Stereo Channel D (Headphones) +device-strings = hw:%f,0,3 +channel-map = left,right +direction = output + +[Mapping analog-stereo-d-input] +description = Analog Stereo Channel D +device-strings = hw:%f,0,3 +channel-map = left,right +direction = input + +[Profile output:analog-stereo-all+input:analog-stereo-all] +description = Analog Stereo Duplex Channels A, B, C (Line/Mic), D (Headphones) +output-mappings = analog-stereo-a analog-stereo-b analog-stereo-c-output analog-stereo-d-output +input-mappings = analog-stereo-a analog-stereo-b analog-stereo-c-input analog-stereo-d-input +priority = 100 +skip-probe = yes + +[Profile output:analog-stereo-d+input:analog-stereo-c] +description = Analog Stereo Channel D (Headphones) Output, Channel C (Line/Mic) Input +output-mappings = analog-stereo-d-output +input-mappings = analog-stereo-c-input +priority = 90 +skip-probe = yes + +[Profile output:analog-stereo-c-d+input:analog-stereo-c-d] +description = Analog Stereo Duplex Channels C (Line/Mic), D (Line/Mic) +output-mappings = analog-stereo-c-output analog-stereo-d-output +input-mappings = analog-stereo-c-input analog-stereo-d-input +priority = 80 +skip-probe = yes + +[Profile output:analog-stereo-a+input:analog-stereo-a] +description = Analog Stereo Duplex Channel A +output-mappings = analog-stereo-a +input-mappings = analog-stereo-a +priority = 50 +skip-probe = yes + +[Profile output:analog-stereo-b+input:analog-stereo-b] +description = Analog Stereo Duplex Channel B +output-mappings = analog-stereo-b +input-mappings = analog-stereo-b +priority = 40 +skip-probe = yes + +[Profile output:analog-stereo-c+input:analog-stereo-c] +description = Analog Stereo Duplex Channel C (Line/Mic) +output-mappings = analog-stereo-c-output +input-mappings = analog-stereo-c-input +priority = 60 +skip-probe = yes + +[Profile output:analog-stereo-d+input:analog-stereo-d] +description = Analog Stereo Duplex Channel D (Headphones) +output-mappings = analog-stereo-d-output +input-mappings = analog-stereo-d-input +priority = 70 +skip-probe = yes + +[Profile output:analog-stereo-a] +description = Analog Stereo Output Channel A +output-mappings = analog-stereo-a +priority = 6 +skip-probe = yes + +[Profile output:analog-stereo-b] +description = Analog Stereo Output Channel B +output-mappings = analog-stereo-b +priority = 5 +skip-probe = yes + +[Profile output:analog-stereo-c] +description = Analog Stereo Output Channel C +output-mappings = analog-stereo-c-output +priority = 7 +skip-probe = yes + +[Profile output:analog-stereo-d] +description = Analog Stereo Output Channel D (Headphones) +output-mappings = analog-stereo-d-output +priority = 8 +skip-probe = yes + +[Profile input:analog-stereo-a] +description = Analog Stereo Input Channel A +input-mappings = analog-stereo-a +priority = 2 +skip-probe = yes + +[Profile input:analog-stereo-b] +description = Analog Stereo Input Channel B +input-mappings = analog-stereo-b +priority = 1 +skip-probe = yes + +[Profile input:analog-stereo-c] +description = Analog Stereo Input Channel C (Line/Mic) +input-mappings = analog-stereo-c-input +priority = 4 +skip-probe = yes + +[Profile input:analog-stereo-d] +description = Analog Stereo Input Channel D +input-mappings = analog-stereo-d-input +priority = 3 +skip-probe = yes -- cgit From 8fe50b0d7cd4a93be85887bdb7efd2663face50d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 04:38:59 +0200 Subject: alsa,core: include mapping name in description strings --- src/modules/alsa/alsa-util.c | 21 ++++++++++++--------- src/pulsecore/sink.c | 35 ++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 0204c28b..0ed4361c 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -752,21 +752,24 @@ void pa_alsa_redirect_errors_dec(void) { } pa_bool_t pa_alsa_init_description(pa_proplist *p) { - const char *s; + const char *d, *k; pa_assert(p); if (pa_device_init_description(p)) return TRUE; - if ((s = pa_proplist_gets(p, "alsa.card_name"))) { - pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s); - return TRUE; - } + if (!(d = pa_proplist_gets(p, "alsa.card_name"))) + d = pa_proplist_gets(p, "alsa.name"); - if ((s = pa_proplist_gets(p, "alsa.name"))) { - pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s); - return TRUE; - } + if (!d) + return FALSE; + + k = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_DESCRIPTION); + + if (d && k) + pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, _("%s %s"), d, k); + else if (d) + pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, d); return FALSE; } diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 7e4513e7..d8f3c7d1 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -2384,30 +2384,35 @@ pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) { } pa_bool_t pa_device_init_description(pa_proplist *p) { - const char *s; + const char *s, *d = NULL, *k; pa_assert(p); if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION)) return TRUE; if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) - if (pa_streq(s, "internal")) { - pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Internal Audio")); - return TRUE; - } + if (pa_streq(s, "internal")) + d = _("Internal Audio"); - if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS))) - if (pa_streq(s, "modem")) { - pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Modem")); - return TRUE; - } + if (!d) + if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS))) + if (pa_streq(s, "modem")) + d = _("Modem"); - if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME))) { - pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s); - return TRUE; - } + if (!d) + d = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME); - return FALSE; + if (!d) + return FALSE; + + k = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_DESCRIPTION); + + if (d && k) + pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, _("%s %s"), d, k); + else if (d) + pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, d); + + return TRUE; } pa_bool_t pa_device_init_intended_roles(pa_proplist *p) { -- cgit From 25e5197f6adb6a371631237a53de15eb4dc1a776 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 15:50:41 +0200 Subject: alsa: merge mic boost into volume slider --- src/modules/alsa/mixer/paths/analog-input-mic.conf.common | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf.common b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common index d67ee4e3..db70e507 100644 --- a/src/modules/alsa/mixer/paths/analog-input-mic.conf.common +++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common @@ -15,6 +15,7 @@ priority = 19 [Element Mic Boost (+20dB)] switch = select +volume = merge [Option Mic Boost (+20dB):on] name = input-boost-on @@ -24,6 +25,7 @@ name = input-boost-off [Element Mic Boost] switch = select +volume = merge [Option Mic Boost:on] name = input-boost-on -- cgit From 348dcd6a31280217cad787f9ee75e607c0f1bf6c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 21:27:02 +0200 Subject: alsa: unify alsa log handling and snd_config_update_free_global() handling in one place --- src/modules/alsa/alsa-sink.c | 1 - src/modules/alsa/alsa-source.c | 2 -- src/modules/alsa/alsa-util.c | 8 +++++--- src/modules/alsa/alsa-util.h | 4 ++-- src/modules/alsa/module-alsa-card.c | 6 ++---- src/modules/alsa/module-alsa-sink.c | 6 ++---- src/modules/alsa/module-alsa-source.c | 6 ++---- 7 files changed, 13 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 34ff6582..281371f7 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -838,7 +838,6 @@ static int unsuspend(struct userdata *u) { pa_log_info("Trying resume..."); - snd_config_update_free_global(); if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK, /*SND_PCM_NONBLOCK|*/ SND_PCM_NO_AUTO_RESAMPLE| diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index c8244c66..4be27eb0 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -787,8 +787,6 @@ static int unsuspend(struct userdata *u) { pa_log_info("Trying resume..."); - snd_config_update_free_global(); - if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, /*SND_PCM_NONBLOCK|*/ SND_PCM_NO_AUTO_RESAMPLE| diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 0ed4361c..1f3e5dcd 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -735,20 +735,22 @@ static void alsa_error_handler(const char *file, int line, const char *function, static pa_atomic_t n_error_handler_installed = PA_ATOMIC_INIT(0); -void pa_alsa_redirect_errors_inc(void) { +void pa_alsa_refcnt_inc(void) { /* This is not really thread safe, but we do our best */ if (pa_atomic_inc(&n_error_handler_installed) == 0) snd_lib_error_set_handler(alsa_error_handler); } -void pa_alsa_redirect_errors_dec(void) { +void pa_alsa_refcnt_dec(void) { int r; pa_assert_se((r = pa_atomic_dec(&n_error_handler_installed)) >= 1); - if (r == 1) + if (r == 1) { snd_lib_error_set_handler(NULL); + snd_config_update_free_global(); + } } pa_bool_t pa_alsa_init_description(pa_proplist *p) { diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index c2f0e5b7..830a922e 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -114,8 +114,8 @@ snd_pcm_t *pa_alsa_open_by_template( void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm); void pa_alsa_dump_status(snd_pcm_t *pcm); -void pa_alsa_redirect_errors_inc(void); -void pa_alsa_redirect_errors_dec(void); +void pa_alsa_refcnt_inc(void); +void pa_alsa_refcnt_dec(void); void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info); void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card); diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index e8a7f206..55f6a6e2 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -287,8 +287,7 @@ int pa__init(pa_module *m) { const char *description; char *fn = NULL; - pa_alsa_redirect_errors_inc(); - snd_config_update_free_global(); + pa_alsa_refcnt_inc(); pa_assert(m); @@ -443,6 +442,5 @@ void pa__done(pa_module*m) { pa_xfree(u); finish: - snd_config_update_free_global(); - pa_alsa_redirect_errors_dec(); + pa_alsa_refcnt_dec(); } diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c index 058ea205..3aa89b2a 100644 --- a/src/modules/alsa/module-alsa-sink.c +++ b/src/modules/alsa/module-alsa-sink.c @@ -82,8 +82,7 @@ int pa__init(pa_module*m) { pa_assert(m); - pa_alsa_redirect_errors_inc(); - snd_config_update_free_global(); + pa_alsa_refcnt_inc(); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); @@ -124,6 +123,5 @@ void pa__done(pa_module*m) { if ((sink = m->userdata)) pa_alsa_sink_free(sink); - snd_config_update_free_global(); - pa_alsa_redirect_errors_dec(); + pa_alsa_refcnt_dec(); } diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c index 3bd1b451..de8bcd0b 100644 --- a/src/modules/alsa/module-alsa-source.c +++ b/src/modules/alsa/module-alsa-source.c @@ -106,8 +106,7 @@ int pa__init(pa_module*m) { pa_assert(m); - pa_alsa_redirect_errors_inc(); - snd_config_update_free_global(); + pa_alsa_refcnt_inc(); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); @@ -148,6 +147,5 @@ void pa__done(pa_module*m) { if ((source = m->userdata)) pa_alsa_source_free(source); - snd_config_update_free_global(); - pa_alsa_redirect_errors_dec(); + pa_alsa_refcnt_dec(); } -- cgit From 8a5f8f41f6524ff36c473712b073f18b08e6483d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 22:58:58 +0200 Subject: alsa: add profile set for Native Instruments 4 DJ --- src/Makefile.am | 2 + .../alsa/mixer/profile-sets/78-pulseaudio.rules | 1 + .../profile-sets/native-instruments-audio4dj.conf | 89 ++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index f3099e26..4068f692 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -116,6 +116,7 @@ EXTRA_DIST = \ map-file \ daemon/org.pulseaudio.policy.in \ modules/alsa/mixer/profile-sets/default.conf \ + modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf \ modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf \ modules/alsa/mixer/profile-sets/78-pulseaudio.rules \ modules/alsa/mixer/paths/analog-input-aux.conf \ @@ -1049,6 +1050,7 @@ modlibexec_LTLIBRARIES += \ alsaprofilesets_DATA = \ modules/alsa/mixer/profile-sets/default.conf \ + modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf \ modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf udevrules_DATA = \ diff --git a/src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules b/src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules index 92a6b034..e3a234f7 100644 --- a/src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules +++ b/src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules @@ -5,5 +5,6 @@ ACTION!="change", GOTO="pulseaudio_end" KERNEL!="card*", GOTO="pulseaudio_end" SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1978", ENV{PULSE_PROFILE_SET}="native-instruments-audio8dj.conf" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="0839", ENV{PULSE_PROFILE_SET}="native-instruments-audio4dj.conf" LABEL="pulseaudio_end" diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf new file mode 100644 index 00000000..3b119a2c --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf @@ -0,0 +1,89 @@ +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Native Instruments Audio 4 DJ +; +; This card has two stereo pairs of input and two stereo pairs of +; output, named channels A and B. Channel B has an additional +; Headphone connector. +; +; We knowingly only define a subset of the theoretically possible +; mapping combinations as profiles here. + +[General] +auto-profiles = no + +[Mapping analog-stereo-a] +description = Analog Stereo Channel A +device-strings = hw:%f,0,0 +channel-map = left,right + +[Mapping analog-stereo-b-output] +description = Analog Stereo Channel B (Headphones) +device-strings = hw:%f,0,1 +channel-map = left,right +direction = output + +[Mapping analog-stereo-b-input] +description = Analog Stereo Channel B +device-strings = hw:%f,0,1 +channel-map = left,right +direction = input + +[Profile output:analog-stereo-all+input:analog-stereo-all] +description = Analog Stereo Duplex Channels A, B (Headphones) +output-mappings = analog-stereo-a analog-stereo-b-output +input-mappings = analog-stereo-a analog-stereo-b-input +priority = 100 +skip-probe = yes + +[Profile output:analog-stereo-a+input:analog-stereo-a] +description = Analog Stereo Duplex Channel A +output-mappings = analog-stereo-a +input-mappings = analog-stereo-a +priority = 40 +skip-probe = yes + +[Profile output:analog-stereo-b+input:analog-stereo-b] +description = Analog Stereo Duplex Channel B (Headphones) +output-mappings = analog-stereo-b-output +input-mappings = analog-stereo-b-input +priority = 50 +skip-probe = yes + +[Profile output:analog-stereo-a] +description = Analog Stereo Output Channel A +output-mappings = analog-stereo-a +priority = 5 +skip-probe = yes + +[Profile output:analog-stereo-b] +description = Analog Stereo Output Channel B (Headphones) +output-mappings = analog-stereo-b-output +priority = 6 +skip-probe = yes + +[Profile input:analog-stereo-a] +description = Analog Stereo Input Channel A +input-mappings = analog-stereo-a +priority = 2 +skip-probe = yes + +[Profile input:analog-stereo-b] +description = Analog Stereo Input Channel B +input-mappings = analog-stereo-b-input +priority = 1 +skip-probe = yes -- cgit From 0fd17c6cb9733e11f81a90d7a5799caaea7357d4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 23:00:53 +0200 Subject: alsa: document default profile set a bit better --- src/modules/alsa/mixer/profile-sets/default.conf | 56 +++++++++++++++++------- 1 file changed, 39 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf index bbe53410..ebd4ff45 100644 --- a/src/modules/alsa/mixer/profile-sets/default.conf +++ b/src/modules/alsa/mixer/profile-sets/default.conf @@ -1,22 +1,43 @@ -# Profile definitions for PulseAudio's ALSA backend +# This file is part of PulseAudio. # -# [Mapping id] -# device-strings = ... -# channel-map = ... -# description = ... -# paths-input = ... -# paths-output = ... -# element-input = ... -# element-output = ... -# priority = ... -# direction = any | input | output +# 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. # -# [Profile id] -# input-mappings = ... -# output-mappings = ... -# description = ... -# priority = ... -# skip-probe = no | yes +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Default profile definitions for the ALSA backend of PulseAudio. This +; is used as fallback for all cards that have no special mapping +; assigned. (and should be good enough for the vast majority of cards) +; +; [General] +; auto-profiles = no | yes # Instead of defining all profiles manually, autogenerate them by combining every input mapping with every output mapping. +; +; [Mapping id] +; device-strings = ... # ALSA device string. %f will be replaced by the card identifier. +; channel-map = ... # Channel mapping to use for this device +; description = ... +; paths-input = ... # A list of mixer paths to use. Every path in this list will be probed. If multiple are found to be working they will be available as device ports +; paths-output = ... +; element-input = ... # Instead of configuring a full mixer path simply configure a single mixer element for volume/mute handling +; element-output = ... +; priority = ... +; direction = any | input | output # Only useful for? +; +; [Profile id] +; input-mappings = ... # Lists mappings for sources on this profile, those mapping must be defined in this file too +; output-mappings = ... # Lists mappings for sinks on this profile, those mappings must be defined in this file too +; description = ... +; priority = ... # Numeric value to deduce priority for this profile +; skip-probe = no | yes # Skip probing for availability? [General] auto-profiles = yes @@ -99,6 +120,7 @@ channel-map = left,right priority = 4 direction = output +; An example for defining multiple-sink profiles #[Profile output:analog-stereo+output:iec958-stereo+input:analog-stereo] #description = Foobar #output-mappings = analog-stereo iec958-stereo -- cgit From 131f76a11db59eb76be301120271fe4c7a816223 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 23:01:24 +0200 Subject: alsa: move mixer config files to subdir in /usr/share --- src/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 4068f692..9afef9d8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,8 +29,8 @@ pulsecoreincludedir=$(includedir)/pulsecore pulseconfdir=$(sysconfdir)/pulse pulselibexecdir=$(libexecdir)/pulse xdgautostartdir=$(sysconfdir)/xdg/autostart -alsaprofilesetsdir=$(datadir)/alsa-mixer/profile-sets -alsapathsdir=$(datadir)/alsa-mixer/paths +alsaprofilesetsdir=$(datadir)/pulseaudio/alsa-mixer/profile-sets +alsapathsdir=$(datadir)/pulseaudio/alsa-mixer/paths udevrulesdir=/lib/udev/rules.d ################################### -- cgit From 11d2a7ee21852d836001e960ffe036f5eda6365b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Jun 2009 23:03:28 +0200 Subject: alsa: move udev rules file behind the other sound related rules --- src/Makefile.am | 4 ++-- src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules | 10 ---------- src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules | 10 ++++++++++ 3 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules create mode 100644 src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 9afef9d8..92453adf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -118,7 +118,7 @@ EXTRA_DIST = \ modules/alsa/mixer/profile-sets/default.conf \ modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf \ modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf \ - modules/alsa/mixer/profile-sets/78-pulseaudio.rules \ + modules/alsa/mixer/profile-sets/90-pulseaudio.rules \ modules/alsa/mixer/paths/analog-input-aux.conf \ modules/alsa/mixer/paths/analog-input.conf \ modules/alsa/mixer/paths/analog-input.conf.common \ @@ -1054,7 +1054,7 @@ alsaprofilesets_DATA = \ modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf udevrules_DATA = \ - modules/alsa/mixer/profile-sets/78-pulseaudio.rules + modules/alsa/mixer/profile-sets/90-pulseaudio.rules alsapaths_DATA = \ modules/alsa/mixer/paths/analog-input-aux.conf \ diff --git a/src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules b/src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules deleted file mode 100644 index e3a234f7..00000000 --- a/src/modules/alsa/mixer/profile-sets/78-pulseaudio.rules +++ /dev/null @@ -1,10 +0,0 @@ -# do not edit this file, it will be overwritten on update - -SUBSYSTEM!="sound", GOTO="pulseaudio_end" -ACTION!="change", GOTO="pulseaudio_end" -KERNEL!="card*", GOTO="pulseaudio_end" - -SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1978", ENV{PULSE_PROFILE_SET}="native-instruments-audio8dj.conf" -SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="0839", ENV{PULSE_PROFILE_SET}="native-instruments-audio4dj.conf" - -LABEL="pulseaudio_end" diff --git a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules new file mode 100644 index 00000000..e3a234f7 --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules @@ -0,0 +1,10 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM!="sound", GOTO="pulseaudio_end" +ACTION!="change", GOTO="pulseaudio_end" +KERNEL!="card*", GOTO="pulseaudio_end" + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1978", ENV{PULSE_PROFILE_SET}="native-instruments-audio8dj.conf" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="0839", ENV{PULSE_PROFILE_SET}="native-instruments-audio4dj.conf" + +LABEL="pulseaudio_end" -- cgit From b70b5edcfc95b5cb258caacc21ffbe255d70ece6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jun 2009 01:30:24 +0200 Subject: alsa: add copyright blobs and comments to all mixer paths/profile sets --- src/modules/alsa/mixer/paths/analog-input-aux.conf | 32 ++++++- src/modules/alsa/mixer/paths/analog-input-fm.conf | 20 +++- .../alsa/mixer/paths/analog-input-linein.conf | 20 +++- .../alsa/mixer/paths/analog-input-mic-line.conf | 21 ++++- src/modules/alsa/mixer/paths/analog-input-mic.conf | 20 +++- .../alsa/mixer/paths/analog-input-mic.conf.common | 20 ++++ .../alsa/mixer/paths/analog-input-tvtuner.conf | 20 +++- .../alsa/mixer/paths/analog-input-video.conf | 32 ++++++- src/modules/alsa/mixer/paths/analog-input.conf | 21 ++++- .../alsa/mixer/paths/analog-input.conf.common | 66 ++++++++----- .../alsa/mixer/paths/analog-output-headphones.conf | 20 +++- .../mixer/paths/analog-output-lfe-on-mono.conf | 22 ++++- .../alsa/mixer/paths/analog-output-mono.conf | 20 +++- src/modules/alsa/mixer/paths/analog-output.conf | 20 +++- .../alsa/mixer/paths/analog-output.conf.common | 103 +++++++++++++++++---- .../alsa/mixer/profile-sets/90-pulseaudio.rules | 16 ++++ src/modules/alsa/mixer/profile-sets/default.conf | 15 ++- .../profile-sets/native-instruments-audio4dj.conf | 2 + .../profile-sets/native-instruments-audio8dj.conf | 2 + 19 files changed, 430 insertions(+), 62 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/mixer/paths/analog-input-aux.conf b/src/modules/alsa/mixer/paths/analog-input-aux.conf index 8f480567..db78eb48 100644 --- a/src/modules/alsa/mixer/paths/analog-input-aux.conf +++ b/src/modules/alsa/mixer/paths/analog-input-aux.conf @@ -1,4 +1,22 @@ -# For devices, where we have an Aux element +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where an 'Aux' element exists +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 90 @@ -29,4 +47,16 @@ override-map.2 = all-left,all-right switch = off volume = off +[Element Mic/Line] +switch = off +volume = off + +[Element TV Tuner] +switch = off +volume = off + +[Element FM] +switch = off +volume = off + .include analog-input.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-fm.conf b/src/modules/alsa/mixer/paths/analog-input-fm.conf index 0f78f39f..baf674aa 100644 --- a/src/modules/alsa/mixer/paths/analog-input-fm.conf +++ b/src/modules/alsa/mixer/paths/analog-input-fm.conf @@ -1,4 +1,22 @@ -# For devices where we have an FM element +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where an 'FM' element exists +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 70 diff --git a/src/modules/alsa/mixer/paths/analog-input-linein.conf b/src/modules/alsa/mixer/paths/analog-input-linein.conf index b6ba738c..4be5722d 100644 --- a/src/modules/alsa/mixer/paths/analog-input-linein.conf +++ b/src/modules/alsa/mixer/paths/analog-input-linein.conf @@ -1,4 +1,22 @@ -# For devices, where we have a Line element +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'Line' element exists +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 90 diff --git a/src/modules/alsa/mixer/paths/analog-input-mic-line.conf b/src/modules/alsa/mixer/paths/analog-input-mic-line.conf index 7d4addf7..f7f30854 100644 --- a/src/modules/alsa/mixer/paths/analog-input-mic-line.conf +++ b/src/modules/alsa/mixer/paths/analog-input-mic-line.conf @@ -1,4 +1,22 @@ -# For devices where we have a Mic/Lineb element +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'Mic/Line' element exists +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 90 @@ -42,3 +60,4 @@ switch = off volume = off .include analog-input.conf.common +.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf b/src/modules/alsa/mixer/paths/analog-input-mic.conf index 004cd24a..2a36f2f3 100644 --- a/src/modules/alsa/mixer/paths/analog-input-mic.conf +++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf @@ -1,4 +1,22 @@ -# For devices where we have a Mic element +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'Mic' element exists +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 100 diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf.common b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common index db70e507..b35e7af8 100644 --- a/src/modules/alsa/mixer/paths/analog-input-mic.conf.common +++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common @@ -1,3 +1,23 @@ +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Common element for all microphone inputs +; +; See analog-output.conf.common for an explanation on the directives + ;;; 'Mic Select' [Element Mic Select] diff --git a/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf b/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf index ea0a0b72..8531ec70 100644 --- a/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf +++ b/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf @@ -1,4 +1,22 @@ -# For devices, where we have a TV Tuner element +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'TV Tuner' element exists +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 70 diff --git a/src/modules/alsa/mixer/paths/analog-input-video.conf b/src/modules/alsa/mixer/paths/analog-input-video.conf index 27acc254..74c76f07 100644 --- a/src/modules/alsa/mixer/paths/analog-input-video.conf +++ b/src/modules/alsa/mixer/paths/analog-input-video.conf @@ -1,4 +1,22 @@ -# For devices, where we have a Video element +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'Video' element exists +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 70 @@ -28,4 +46,16 @@ volume = merge override-map.1 = all override-map.2 = all-left,all-right +[Element Mic/Line] +switch = off +volume = off + +[Element TV Tuner] +switch = off +volume = off + +[Element FM] +switch = off +volume = off + .include analog-input.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input.conf b/src/modules/alsa/mixer/paths/analog-input.conf index b221bb44..5055f90a 100644 --- a/src/modules/alsa/mixer/paths/analog-input.conf +++ b/src/modules/alsa/mixer/paths/analog-input.conf @@ -1,4 +1,23 @@ -# A fallback for devices that lack seperate Mic/Line/Aux/Video elements +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; A fallback for devices that lack seperate Mic/Line/Aux/Video/TV +; Tuner/FM elements +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 100 diff --git a/src/modules/alsa/mixer/paths/analog-input.conf.common b/src/modules/alsa/mixer/paths/analog-input.conf.common index d34afd04..6728a6ae 100644 --- a/src/modules/alsa/mixer/paths/analog-input.conf.common +++ b/src/modules/alsa/mixer/paths/analog-input.conf.common @@ -1,30 +1,48 @@ -# Mixer path for PulseAudio's ALSA backend. If multiple options by the -# same id are discovered they will be suffixed with a number to -# distuingish them, in the same order they appear here. +# This file is part of PulseAudio. # -# Source selection should use the following names: +# 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. # -# input -- If we don't know the exact kind of input -# input-microphone -# input-microphone-internal -# input-microphone-external -# input-linein -# input-video -# input-radio -# input-docking-microphone -# input-docking-linein -# input-docking -# -# We explicitly don't want to wrap the following sources: -# -# CD -# Synth/MIDI -# Phone -# Mix -# Digital/SPDIF -# Master -# PC Speaker +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Mixer path for PulseAudio's ALSA backend, common elements for all +; input paths. If multiple options by the same id are discovered they +; will be suffixed with a number to distuingish them, in the same +; order they appear here. +; +; Source selection should use the following names: +; +; input -- If we don't know the exact kind of input +; input-microphone +; input-microphone-internal +; input-microphone-external +; input-linein +; input-video +; input-radio +; input-docking-microphone +; input-docking-linein +; input-docking +; +; We explicitly don't want to wrap the following sources: +; +; CD +; Synth/MIDI +; Phone +; Mix +; Digital/SPDIF +; Master +; PC Speaker +; +; See analog-output.conf.common for an explanation on the directives ;;; 'Input Source Select' diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf index 1a172d4c..c018e0eb 100644 --- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf +++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf @@ -1,4 +1,22 @@ -# Path for mixers that have a Headphone slider +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Path for mixers that have a 'Headphone' control +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 90 diff --git a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf index 67031762..7a267890 100644 --- a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf +++ b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf @@ -1,5 +1,23 @@ -# Intended for usage in laptops that have a seperate LFE speaker -# connected to the Master mono connector +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Intended for usage in laptops that have a seperate LFE speaker +; connected to the Master mono connector +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 40 diff --git a/src/modules/alsa/mixer/paths/analog-output-mono.conf b/src/modules/alsa/mixer/paths/analog-output-mono.conf index a23d9b79..f6cb9f8a 100644 --- a/src/modules/alsa/mixer/paths/analog-output-mono.conf +++ b/src/modules/alsa/mixer/paths/analog-output-mono.conf @@ -1,4 +1,22 @@ -# Intended for usage on boards that have a seperate Mono output plug. +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Intended for usage on boards that have a seperate Mono output plug. +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 50 diff --git a/src/modules/alsa/mixer/paths/analog-output.conf b/src/modules/alsa/mixer/paths/analog-output.conf index 15e703c4..ea108aaf 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf +++ b/src/modules/alsa/mixer/paths/analog-output.conf @@ -1,4 +1,22 @@ -# Intended for the 'default' output +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Intended for the 'default' output +; +; See analog-output.conf.common for an explanation on the directives [General] priority = 100 diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common index c38eccde..47243dbc 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf.common +++ b/src/modules/alsa/mixer/paths/analog-output.conf.common @@ -1,26 +1,89 @@ -# Common part of all paths - -# [General] -# priority = ... -# description = ... -# -# [Option ...:...] -# name = ... -# priority = ... -# -# [Element ...] -# required = ignore | switch | volume | enumeration | any -# required-absent = ignore | switch | volume +# This file is part of PulseAudio. # -# switch = ignore | mute | off | on | select -# volume = ignore | merge | off | zero -# enumeration = ignore | select +# 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. # -# direction = playback | capture -# direction-try-other = no | yes +# 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. # -# override-map.1 = ... -# override-map.2 = ... +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Common part of all paths + +; So here's generally how mixer paths are used by PA: PA goes through +; a mixer path file from top to bottom and checks if a mixer element +; described therein exists. If so it is added to the list of mixer +; elements PA will control, keeping the order it read them in. If a +; mixer element described here has set the required= or +; required-absent= directives a path might not be accepted as valid +; and is ignored in its entirety (see below). However usually if a +; element listed here is missing this one element is ignored but not +; the entire path. +; +; When a device shall be muted/unmuted *all* elements listed in a path +; file with "switch = mute" will be toggled. +; +; When a device shall change its volume, PA will got through the list +; of all elements with "volume = merge" and set the volume on the +; first element. If that element does not support dB volumes, this is +; where the story ends. If it does support dB volumes, PA divides the +; requested volume by the volume that was set on this element, and +; then go on to the next element with "volume = merge" and then set +; that there, and so on. That way the first volume element in the +; path will be the one that does the 'biggest' part of the overall +; volume adjustment, with the remaining elements usually being set to +; some value next to 0dB. This logic makes sure we get the full range +; over all volume sliders and a very high granularity of volumes +; already in hardware. +; +; All switches and enumerations set to "select" are exposed via the +; "port" functionality of sinks/sources. Basically every possible +; switch setting and every possible enumeration setting will be +; combined and made into a "port". So make sure you don't list too +; many switches/enums for exposing, because the number of ports might +; rise exponentially. + +; [General] +; priority = ... # Priority for this path +; description = ... +; +; [Option ...:...] # For each option of an enumeration or switch element +; # that shall be exposed as a sink/source port. Needs to +; # be named after the Element, followed by a colon, followed +; # by the option name, resp. on/off if the element is a switch. +; name = ... # Logical name to use in the path identifier +; priority = ... # Priority if this is made into a device port +; +; [Element ...] # For each element that we shall control +; required = ignore | switch | volume | enumeration | any # If set, require this element to be of this kind and available, +; # otherwise don't consider this path valid for the card +; required-absent = ignore | switch | volume # If set, require this element to not be of this kind and not +; # available, otherwise don't consider this path valid for the card +; +; switch = ignore | mute | off | on | select # What to do with this switch: ignore it, make it follow mute status, +; # always set it to off, always to on, or make it selectable as port. +; # If set to 'select' you need to define an Option section for on +; # and off +; volume = ignore | merge | off | zero # What to do with this volume: ignore it, merge it into the device +; # volume slider, always set it to the lowest value possible, or always +; # set it to 0 dB (for whatever that means) +; enumeration = ignore | select # What to do with this enumeration, ignore it or make it selectable +; # via device ports. If set to 'select' you need to define an Option section +; # for each of the items you want to expose +; direction = playback | capture # Is this relevant only for playback or capture? If not set this will implicitly be +; # set the direction of the PCM device is opened as. Generally this doesn't need to be set +; # unless you have a broken driver that has playback controls marked for capture or vice +; # versa +; direction-try-other = no | yes # If the element does not supported what is requested, try the other direction, too? +; +; override-map.1 = ... # Override the channel mask of the mixer control if the control only exposes a single channel +; override-map.2 = ... # Override the channel masks of the mixer control if the control only exposes two channels [Element PCM] switch = mute diff --git a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules index e3a234f7..ea1a2fed 100644 --- a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules +++ b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules @@ -1,5 +1,21 @@ # do not edit this file, it will be overwritten on update +# This file is part of PulseAudio. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + SUBSYSTEM!="sound", GOTO="pulseaudio_end" ACTION!="change", GOTO="pulseaudio_end" KERNEL!="card*", GOTO="pulseaudio_end" diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf index ebd4ff45..6ae11b47 100644 --- a/src/modules/alsa/mixer/profile-sets/default.conf +++ b/src/modules/alsa/mixer/profile-sets/default.conf @@ -19,22 +19,27 @@ ; assigned. (and should be good enough for the vast majority of cards) ; ; [General] -; auto-profiles = no | yes # Instead of defining all profiles manually, autogenerate them by combining every input mapping with every output mapping. +; auto-profiles = no | yes # Instead of defining all profiles manually, autogenerate +; # them by combining every input mapping with every output mapping. ; ; [Mapping id] ; device-strings = ... # ALSA device string. %f will be replaced by the card identifier. ; channel-map = ... # Channel mapping to use for this device ; description = ... -; paths-input = ... # A list of mixer paths to use. Every path in this list will be probed. If multiple are found to be working they will be available as device ports +; paths-input = ... # A list of mixer paths to use. Every path in this list will be probed. +; # If multiple are found to be working they will be available as device ports ; paths-output = ... -; element-input = ... # Instead of configuring a full mixer path simply configure a single mixer element for volume/mute handling +; element-input = ... # Instead of configuring a full mixer path simply configure a single +; # mixer element for volume/mute handling ; element-output = ... ; priority = ... ; direction = any | input | output # Only useful for? ; ; [Profile id] -; input-mappings = ... # Lists mappings for sources on this profile, those mapping must be defined in this file too -; output-mappings = ... # Lists mappings for sinks on this profile, those mappings must be defined in this file too +; input-mappings = ... # Lists mappings for sources on this profile, those mapping must be +; # defined in this file too +; output-mappings = ... # Lists mappings for sinks on this profile, those mappings must be +; # defined in this file too ; description = ... ; priority = ... # Numeric value to deduce priority for this profile ; skip-probe = no | yes # Skip probing for availability? diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf index 3b119a2c..2b835308 100644 --- a/src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf +++ b/src/modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf @@ -22,6 +22,8 @@ ; ; We knowingly only define a subset of the theoretically possible ; mapping combinations as profiles here. +; +; See default.conf for an explanation on the directives used here. [General] auto-profiles = no diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf index fe1e9ae8..3fe3cc56 100644 --- a/src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf +++ b/src/modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf @@ -22,6 +22,8 @@ ; ; We knowingly only define a subset of the theoretically possible ; mapping combinations as profiles here. +; +; See default.conf for an explanation on the directives used here. [General] auto-profiles = no -- cgit From b1ea7c0d89a9516f89a7eed5d7a2f121d639b10a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jun 2009 01:55:59 +0200 Subject: alsa: document alsa mixer path/profile sets a bit more --- src/modules/alsa/mixer/paths/analog-output.conf.common | 8 ++++++++ src/modules/alsa/mixer/profile-sets/default.conf | 18 +++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common index 47243dbc..cc1185f4 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf.common +++ b/src/modules/alsa/mixer/paths/analog-output.conf.common @@ -48,6 +48,10 @@ ; combined and made into a "port". So make sure you don't list too ; many switches/enums for exposing, because the number of ports might ; rise exponentially. +; +; Only one path can be selected at a time. All paths that are valid +; for an audio device will be exposed as "port" for the sink/source. + ; [General] ; priority = ... # Priority for this path @@ -84,6 +88,10 @@ ; ; override-map.1 = ... # Override the channel mask of the mixer control if the control only exposes a single channel ; override-map.2 = ... # Override the channel masks of the mixer control if the control only exposes two channels +; # Override maps should list for each element channel which high-level channels it controls via a +; # channel mask. A channel mask may either be the name of a single channel, or the words "all-left", +; # "all-right", "all-center", "all-front", "all-rear", and "all" to encode a specific subset of +; # channels in a mask [Element PCM] switch = mute diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf index 6ae11b47..ac41a8d3 100644 --- a/src/modules/alsa/mixer/profile-sets/default.conf +++ b/src/modules/alsa/mixer/profile-sets/default.conf @@ -16,8 +16,18 @@ ; Default profile definitions for the ALSA backend of PulseAudio. This ; is used as fallback for all cards that have no special mapping -; assigned. (and should be good enough for the vast majority of cards) -; +; assigned. (and should be good enough for the vast majority of +; cards). Use the udev property PULSE_PROFILE_SET to assign a +; different profile set than this one to a device. So what is this +; about? Simply, what we do here is map ALSA devices to how they are +; exposed in PA. We say which ALSA device string to use to open a +; device, which channel mapping to use then, and which mixer path to +; use. This is encoded in a 'mapping'. Multiple of these mappings can +; be bound together in a 'profile' which is then directly exposed in +; the UI as a card profile. Each mapping assigned to a profile will +; result in one sink/source to be created if the profile is selected +; for the card. + ; [General] ; auto-profiles = no | yes # Instead of defining all profiles manually, autogenerate ; # them by combining every input mapping with every output mapping. @@ -42,7 +52,9 @@ ; # defined in this file too ; description = ... ; priority = ... # Numeric value to deduce priority for this profile -; skip-probe = no | yes # Skip probing for availability? +; skip-probe = no | yes # Skip probing for availability? If this is yes then this profile +; # will be assumed as working without probing. Makes initialization +; # a bit faster but only works if the card is really known well. [General] auto-profiles = yes -- cgit From 6ad38556395fa1ee2020abb258e5f814fef8ad34 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jun 2009 02:03:03 +0200 Subject: alsa: synthesize volume values more sensibly for channels that are not controllable in hw --- src/modules/alsa/alsa-mixer.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index def26221..a5515e1b 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -479,6 +479,7 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann snd_mixer_elem_t *me; snd_mixer_selem_channel_id_t c; pa_channel_position_mask_t mask = 0; + pa_volume_t max_channel_volume = PA_VOLUME_MUTED; unsigned k; pa_assert(m); @@ -545,6 +546,9 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann f = from_alsa_volume(value, e->min_volume, e->max_volume); } + if (f > max_channel_volume) + max_channel_volume = f; + for (k = 0; k < cm->channels; k++) if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) if (v->values[k] < f) @@ -555,7 +559,7 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann for (k = 0; k < cm->channels; k++) if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k]))) - v->values[k] = PA_VOLUME_NORM; + v->values[k] = max_channel_volume; return 0; } @@ -677,6 +681,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann snd_mixer_elem_t *me; snd_mixer_selem_channel_id_t c; pa_channel_position_mask_t mask = 0; + pa_volume_t max_channel_volume = PA_VOLUME_MUTED; unsigned k; pa_assert(m); @@ -696,11 +701,21 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) { int r; pa_volume_t f = PA_VOLUME_MUTED; + pa_bool_t found = FALSE; for (k = 0; k < cm->channels; k++) - if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) + if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) { + found = TRUE; if (v->values[k] > f) f = v->values[k]; + } + + if (!found) { + /* Hmm, so this channel does not exist in the volume + * struct, so let's bind it to the overall max of the + * volume. */ + f = pa_cvolume_max(v); + } if (e->has_dB) { long value = to_alsa_dB(f); @@ -756,6 +771,9 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann f = from_alsa_volume(value, e->min_volume, e->max_volume); } + if (f > max_channel_volume) + max_channel_volume = f; + for (k = 0; k < cm->channels; k++) if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) if (rv.values[k] < f) @@ -766,7 +784,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann for (k = 0; k < cm->channels; k++) if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k]))) - rv.values[k] = PA_VOLUME_NORM; + rv.values[k] = max_channel_volume; *v = rv; return 0; -- cgit From 8474fd7c62712470465800ddccd6ea291d04151e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jun 2009 04:19:08 +0200 Subject: core: ask RealtimeKit for RT scheduling --- src/Makefile.am | 6 ++ src/pulsecore/core-util.c | 96 ++++++++++++++++++----- src/pulsecore/rtkit.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/rtkit.h | 62 +++++++++++++++ 4 files changed, 335 insertions(+), 18 deletions(-) create mode 100644 src/pulsecore/rtkit.c create mode 100644 src/pulsecore/rtkit.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 92453adf..59f5fff7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -573,6 +573,7 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/conf-parser.c pulsecore/conf-parser.h \ pulsecore/core-error.c pulsecore/core-error.h \ pulsecore/core-util.c pulsecore/core-util.h \ + pulsecore/rtkit.c pulsecore/rtkit.h \ pulsecore/creds.h \ pulsecore/dynarray.c pulsecore/dynarray.h \ pulsecore/endianmacros.h \ @@ -1735,6 +1736,11 @@ update-reserve: wget -O modules/$$i http://git.0pointer.de/\?p=reserve.git\;a=blob_plain\;f=$$i\;hb=master ; \ done +update-rtkit: + for i in rtkit.c rtkit.h ; do \ + wget -O pulsecore/$$i http://git.0pointer.de/\?p=rtkit.git\;a=blob_plain\;f=$$i\;hb=master ; \ + done + # Automatically generate linker version script. We use the same one for all public .sos update-map-file: ( echo "PULSE_0 {" ; \ diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index a71ba0b0..1fdd1890 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -50,6 +50,10 @@ #ifdef HAVE_SCHED_H #include + +#if defined(__linux__) && !defined(SCHED_RESET_ON_FORK) +#define SCHED_RESET_ON_FORK 0x40000000 +#endif #endif #ifdef HAVE_SYS_RESOURCE_H @@ -92,6 +96,10 @@ #include #endif +#ifdef HAVE_DBUS +#include "rtkit.h" +#endif + #include #include #include @@ -552,16 +560,68 @@ char *pa_strlcpy(char *b, const char *s, size_t l) { return b; } +static int set_scheduler(int rtprio) { + struct sched_param sp; + int r; +#ifdef HAVE_DBUS + DBusError error; + DBusConnection *bus; + + dbus_error_init(&error); +#endif + + pa_zero(sp); + sp.sched_priority = rtprio; + +#ifdef SCHED_RESET_ON_FORK + if ((r = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp)) == 0) { + pa_log_debug("SCHED_RR|SCHED_RESET_ON_FORK worked."); + return 0; + } +#endif + + if ((r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp)) == 0) { + pa_log_debug("SCHED_RR worked."); + return 0; + } + +#ifdef HAVE_DBUS + /* Try to talk to RealtimeKit */ + + if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) { + pa_log("Failed to connect to system bus: %s\n", error.message); + dbus_error_free(&error); + errno = -EIO; + return -1; + } + + r = rtkit_make_realtime(bus, 0, rtprio); + dbus_connection_unref(bus); + + if (r >= 0) { + pa_log_debug("RealtimeKit worked."); + return 0; + } + + errno = -r; +#else + errno = r; +#endif + + return -1; +} + /* Make the current thread a realtime thread, and acquire the highest * rtprio we can get that is less or equal the specified parameter. If * the thread is already realtime, don't do anything. */ int pa_make_realtime(int rtprio) { #ifdef _POSIX_PRIORITY_SCHEDULING - struct sched_param sp; int r, policy; + struct sched_param sp; + int rtprio_try; - memset(&sp, 0, sizeof(sp)); + pa_zero(sp); policy = 0; if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) { @@ -569,29 +629,29 @@ int pa_make_realtime(int rtprio) { return -1; } - if (policy == SCHED_FIFO && sp.sched_priority >= rtprio) { - pa_log_info("Thread already being scheduled with SCHED_FIFO with priority %i.", sp.sched_priority); +#ifdef SCHED_RESET_ON_FORK + policy &= ~SCHED_RESET_ON_FORK; +#endif + + if ((policy == SCHED_FIFO || policy == SCHED_RR) && sp.sched_priority >= rtprio) { + pa_log_info("Thread already being scheduled with SCHED_FIFO/SCHED_RR with priority %i.", sp.sched_priority); return 0; } - sp.sched_priority = rtprio; - if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) != 0) { - - while (sp.sched_priority > 1) { - sp.sched_priority --; + if (set_scheduler(rtprio) >= 0) { + pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i.", rtprio); + return 0; + } - if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) == 0) { - pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i, which is lower than the requested %i.", sp.sched_priority, rtprio); - return 0; - } + for (rtprio_try = rtprio-1; rtprio_try >= 1; rtprio_try--) { + if (set_scheduler(rtprio_try)) { + pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", rtprio_try, rtprio); + return 0; } - - pa_log_warn("pthread_setschedparam(): %s", pa_cstrerror(r)); - return -1; } - pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i.", sp.sched_priority); - return 0; + pa_log_warn("Failed to acquire real-time scheduling: %s", pa_cstrerror(r)); + return -1; #else errno = ENOTSUP; diff --git a/src/pulsecore/rtkit.c b/src/pulsecore/rtkit.c new file mode 100644 index 00000000..aecc4e32 --- /dev/null +++ b/src/pulsecore/rtkit.c @@ -0,0 +1,189 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + Copyright 2009 Lennart Poettering + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include + +#include "rtkit.h" + +#ifdef __linux__ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include + +static pid_t _gettid(void) { + return (pid_t) syscall(SYS_gettid); +} + +static int translate_error(const char *name) { + if (strcmp(name, DBUS_ERROR_NO_MEMORY) == 0) + return -ENOMEM; + if (strcmp(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 || + strcmp(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0) + return -ENOENT; + if (strcmp(name, DBUS_ERROR_ACCESS_DENIED) == 0 || + strcmp(name, DBUS_ERROR_AUTH_FAILED) == 0) + return -EACCES; + + return -EIO; +} + +int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) { + DBusMessage *m = NULL, *r = NULL; + dbus_uint64_t u64; + dbus_uint32_t u32; + DBusError error; + int ret; + + dbus_error_init(&error); + + if (thread == 0) + thread = _gettid(); + + if (!(m = dbus_message_new_method_call( + RTKIT_SERVICE_NAME, + RTKIT_OBJECT_PATH, + "org.freedesktop.RealtimeKit1", + "MakeThreadRealtime"))) { + ret = -ENOMEM; + goto finish; + } + + u64 = (dbus_uint64_t) thread; + u32 = (dbus_uint32_t) priority; + + if (!dbus_message_append_args( + m, + DBUS_TYPE_UINT64, &u64, + DBUS_TYPE_UINT32, &u32, + DBUS_TYPE_INVALID)) { + ret = -ENOMEM; + goto finish; + } + + if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) { + ret = translate_error(error.name); + goto finish; + } + + + if (dbus_set_error_from_message(&error, r)) { + ret = translate_error(error.name); + goto finish; + } + + ret = 0; + +finish: + + if (m) + dbus_message_unref(m); + + if (r) + dbus_message_unref(r); + + dbus_error_free(&error); + + return ret; +} + +int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) { + DBusMessage *m = NULL, *r = NULL; + dbus_uint64_t u64; + dbus_int32_t s32; + DBusError error; + int ret; + + dbus_error_init(&error); + + if (thread == 0) + thread = _gettid(); + + if (!(m = dbus_message_new_method_call( + RTKIT_SERVICE_NAME, + RTKIT_OBJECT_PATH, + "org.freedesktop.RealtimeKit1", + "MakeThreadHighPriority"))) { + ret = -ENOMEM; + goto finish; + } + + u64 = (dbus_uint64_t) thread; + s32 = (dbus_int32_t) nice_level; + + if (!dbus_message_append_args( + m, + DBUS_TYPE_UINT64, &u64, + DBUS_TYPE_INT32, &s32, + DBUS_TYPE_INVALID)) { + ret = -ENOMEM; + goto finish; + } + + + + if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) { + ret = translate_error(error.name); + goto finish; + } + + + if (dbus_set_error_from_message(&error, r)) { + ret = translate_error(error.name); + goto finish; + } + + ret = 0; + +finish: + + if (m) + dbus_message_unref(m); + + if (r) + dbus_message_unref(r); + + dbus_error_free(&error); + + return ret; +} + +#else + +int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) { + return -ENOTSUP; +} + +int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) { + return -ENOTSUP; +} + +#endif diff --git a/src/pulsecore/rtkit.h b/src/pulsecore/rtkit.h new file mode 100644 index 00000000..2081b4e9 --- /dev/null +++ b/src/pulsecore/rtkit.h @@ -0,0 +1,62 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foortkithfoo +#define foortkithfoo + +/*** + Copyright 2009 Lennart Poettering + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the reference implementation for a client for + * RealtimeKit. You don't have to use this, but if do, just copy these + * sources into your repository */ + +#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1" +#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1" + +/* This is mostly equivalent to sched_setparam(thread, SCHED_RR, { + * .sched_priority = priority }). 'thread' needs to be a kernel thread + * id as returned by gettid(), not a pthread_t! If 'thread' is 0 the + * current thread is used. The returned value is a negative errno + * style error code, or 0 on success. */ +int rtkit_make_realtime(DBusConnection *system_bus, pid_t thread, int priority); + +/* This is mostly equivalent to setpriority(PRIO_PROCESS, thread, + * nice_level). 'thread' needs to be a kernel thread id as returned by + * gettid(), not a pthread_t! If 'thread' is 0 the current thread is + * used. The returned value is a negative errno style error code, or 0 + * on success.*/ +int rtkit_make_high_priority(DBusConnection *system_bus, pid_t thread, int nice_level); + +#ifdef __cplusplus +} +#endif + +#endif -- cgit From bacc5ca6f48f280c781af508250a25bb2012327f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jun 2009 04:45:19 +0200 Subject: core: use rtkit to acquire high-priority scheduling --- src/pulsecore/core-util.c | 150 ++++++++++++++-------------------------------- src/pulsecore/core-util.h | 3 - 2 files changed, 46 insertions(+), 107 deletions(-) (limited to 'src') diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 1fdd1890..4550344f 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -617,40 +617,20 @@ static int set_scheduler(int rtprio) { int pa_make_realtime(int rtprio) { #ifdef _POSIX_PRIORITY_SCHEDULING - int r, policy; - struct sched_param sp; - int rtprio_try; - - pa_zero(sp); - policy = 0; - - if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) { - pa_log("pthread_getschedgetparam(): %s", pa_cstrerror(r)); - return -1; - } - -#ifdef SCHED_RESET_ON_FORK - policy &= ~SCHED_RESET_ON_FORK; -#endif - - if ((policy == SCHED_FIFO || policy == SCHED_RR) && sp.sched_priority >= rtprio) { - pa_log_info("Thread already being scheduled with SCHED_FIFO/SCHED_RR with priority %i.", sp.sched_priority); - return 0; - } + int p; if (set_scheduler(rtprio) >= 0) { pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i.", rtprio); return 0; } - for (rtprio_try = rtprio-1; rtprio_try >= 1; rtprio_try--) { - if (set_scheduler(rtprio_try)) { - pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", rtprio_try, rtprio); + for (p = rtprio-1; p >= 1; p--) + if (set_scheduler(p)) { + pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", p, rtprio); return 0; } - } - pa_log_warn("Failed to acquire real-time scheduling: %s", pa_cstrerror(r)); + pa_log_info("Failed to acquire real-time scheduling: %s", pa_cstrerror(errno)); return -1; #else @@ -659,80 +639,42 @@ int pa_make_realtime(int rtprio) { #endif } -/* This is merely used for giving the user a hint. This is not correct - * for anything security related */ -pa_bool_t pa_can_realtime(void) { - - if (geteuid() == 0) - return TRUE; - -#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO) - { - struct rlimit rl; +static int set_nice(int nice_level) { +#ifdef HAVE_DBUS + DBusError error; + DBusConnection *bus; + int r; - if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) - if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY) - return TRUE; - } + dbus_error_init(&error); #endif -#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE) - { - cap_t cap; - - if ((cap = cap_get_proc())) { - cap_flag_value_t flag = CAP_CLEAR; - - if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0) - if (flag == CAP_SET) { - cap_free(cap); - return TRUE; - } - - cap_free(cap); - } + if (setpriority(PRIO_PROCESS, 0, nice_level) >= 0) { + pa_log_debug("setpriority() worked."); + return 0; } -#endif - - return FALSE; -} - -/* This is merely used for giving the user a hint. This is not correct - * for anything security related */ -pa_bool_t pa_can_high_priority(void) { - if (geteuid() == 0) - return TRUE; - -#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO) - { - struct rlimit rl; +#ifdef HAVE_DBUS + /* Try to talk to RealtimeKit */ - if (getrlimit(RLIMIT_NICE, &rl) >= 0) - if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY) - return TRUE; + if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) { + pa_log("Failed to connect to system bus: %s\n", error.message); + dbus_error_free(&error); + errno = -EIO; + return -1; } -#endif -#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE) - { - cap_t cap; - - if ((cap = cap_get_proc())) { - cap_flag_value_t flag = CAP_CLEAR; - - if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0) - if (flag == CAP_SET) { - cap_free(cap); - return TRUE; - } + r = rtkit_make_high_priority(bus, 0, nice_level); + dbus_connection_unref(bus); - cap_free(cap); - } + if (r >= 0) { + pa_log_debug("RealtimeKit worked."); + return 0; } + + errno = -r; #endif - return FALSE; + return -1; } /* Raise the priority of the current process as much as possible that @@ -740,22 +682,21 @@ pa_bool_t pa_can_high_priority(void) { int pa_raise_priority(int nice_level) { #ifdef HAVE_SYS_RESOURCE_H - if (setpriority(PRIO_PROCESS, 0, nice_level) < 0) { - int n; + int n; - for (n = nice_level+1; n < 0; n++) { + if (set_nice(nice_level) >= 0) { + pa_log_info("Successfully gained nice level %i.", nice_level); + return 0; + } - if (setpriority(PRIO_PROCESS, 0, n) == 0) { - pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level); - return 0; - } + for (n = nice_level+1; n < 0; n++) + if (set_nice(n) > 0) { + pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level); + return 0; } - pa_log_warn("setpriority(): %s", pa_cstrerror(errno)); - return -1; - } - - pa_log_info("Successfully gained nice level %i.", nice_level); + pa_log_info("Failed to acquire high-priority scheduling: %s", pa_cstrerror(errno)); + return -1; #endif #ifdef OS_IS_WIN32 @@ -763,9 +704,10 @@ int pa_raise_priority(int nice_level) { if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) { pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError()); errno = EPERM; - return .-1; - } else - pa_log_info("Successfully gained high priority class."); + return -1; + } + + pa_log_info("Successfully gained high priority class."); } #endif @@ -780,8 +722,8 @@ void pa_reset_priority(void) { setpriority(PRIO_PROCESS, 0, 0); - memset(&sp, 0, sizeof(sp)); - pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp) == 0); + pa_zero(sp); + pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp); #endif #ifdef OS_IS_WIN32 diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index b841edbb..96a0480a 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -80,9 +80,6 @@ int pa_make_realtime(int rtprio); int pa_raise_priority(int nice_level); void pa_reset_priority(void); -pa_bool_t pa_can_realtime(void); -pa_bool_t pa_can_high_priority(void); - int pa_parse_boolean(const char *s) PA_GCC_PURE; static inline const char *pa_yes_no(pa_bool_t b) { -- cgit From 9c438bcac67197b685c57c2f6f1b59cdfbf7c9b2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jun 2009 17:37:18 +0200 Subject: daemon: strip all special suid/caps log from our startup code, we'll now rely on RealtimeKit for all high-priority/RT scheduling policy issues --- src/Makefile.am | 17 +---- src/daemon/caps.c | 99 +++++---------------------- src/daemon/caps.h | 3 - src/daemon/main.c | 197 +++++------------------------------------------------- 4 files changed, 31 insertions(+), 285 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 59f5fff7..5efe1ac8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -114,7 +114,6 @@ EXTRA_DIST = \ modules/module-defs.h.m4 \ daemon/pulseaudio.desktop.in \ map-file \ - daemon/org.pulseaudio.policy.in \ modules/alsa/mixer/profile-sets/default.conf \ modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf \ modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf \ @@ -184,16 +183,6 @@ else pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f)) endif -if HAVE_POLKIT -policy_in_files = daemon/org.pulseaudio.policy.in -policy_DATA = $(policy_in_files:.policy.in=.policy) -@INTLTOOL_POLICY_RULE@ - -pulseaudio_SOURCES += daemon/polkit.c daemon/polkit.h -pulseaudio_CFLAGS += $(POLKIT_CFLAGS) -pulseaudio_LDADD += $(POLKIT_LIBS) -endif - ################################### # Utility programs # ################################### @@ -1655,11 +1644,7 @@ module_rygel_media_server_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) # Some minor stuff # ################################### -suid: pulseaudio .libs/lt-pulseaudio - chown root $^ - chmod u+s $^ - -CLEANFILES = esdcompat client.conf default.pa system.pa daemon.conf start-pulseaudio-x11 daemon/pulseaudio.desktop daemon/org.pulseaudio.policy +CLEANFILES = esdcompat client.conf default.pa system.pa daemon.conf start-pulseaudio-x11 daemon/pulseaudio.desktop esdcompat: daemon/esdcompat.in Makefile sed -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \ diff --git a/src/daemon/caps.c b/src/daemon/caps.c index d2ae8d0e..294be494 100644 --- a/src/daemon/caps.c +++ b/src/daemon/caps.c @@ -39,6 +39,7 @@ #ifdef HAVE_SYS_CAPABILITY_H #include #endif + #ifdef HAVE_SYS_PRCTL_H #include #endif @@ -51,12 +52,13 @@ int setresgid(gid_t r, gid_t e, gid_t s); int setresuid(uid_t r, uid_t e, uid_t s); #endif -#ifdef HAVE_GETUID - /* Drop root rights when called SUID root */ void pa_drop_root(void) { - uid_t uid = getuid(); +#ifdef HAVE_GETUID + uid_t uid; + + uid = getuid(); if (uid == 0 || geteuid() != 0) return; @@ -73,90 +75,19 @@ void pa_drop_root(void) { pa_assert_se(getuid() == uid); pa_assert_se(geteuid() == uid); -} - -#else - -void pa_drop_root(void) { -} - -#endif - -#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_SYS_PRCTL_H) - -/* Limit permitted capabilities set to CAPSYS_NICE */ -void pa_limit_caps(void) { - cap_t caps; - cap_value_t nice_cap = CAP_SYS_NICE; - - pa_assert_se(caps = cap_init()); - pa_assert_se(cap_clear(caps) == 0); - pa_assert_se(cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET) == 0); - pa_assert_se(cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET) == 0); - - if (cap_set_proc(caps) < 0) - /* Hmm, so we couldn't limit our caps, which probably means we - * hadn't any in the first place, so let's just make sure of - * that */ - pa_drop_caps(); - else - pa_log_info(_("Limited capabilities successfully to CAP_SYS_NICE.")); - - pa_assert_se(cap_free(caps) == 0); - - pa_assert_se(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0); -} - -/* Drop all capabilities, effectively becoming a normal user */ -void pa_drop_caps(void) { - cap_t caps; - -#ifndef __OPTIMIZE__ - /* Valgrind doesn't not know set_caps, so we bypass it here -- but - * only in development builds.*/ - - if (pa_in_valgrind() && !pa_have_caps()) - return; #endif +#ifdef HAVE_SYS_PRCTL_H pa_assert_se(prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == 0); - - pa_assert_se(caps = cap_init()); - pa_assert_se(cap_clear(caps) == 0); - pa_assert_se(cap_set_proc(caps) == 0); - pa_assert_se(cap_free(caps) == 0); - - pa_assert_se(!pa_have_caps()); -} - -pa_bool_t pa_have_caps(void) { - cap_t caps; - cap_flag_value_t flag = CAP_CLEAR; - -#ifdef __OPTIMIZE__ - pa_assert_se(caps = cap_get_proc()); -#else - if (!(caps = cap_get_proc())) - return FALSE; #endif - pa_assert_se(cap_get_flag(caps, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0); - pa_assert_se(cap_free(caps) == 0); - - return flag == CAP_SET; -} - -#else - -/* NOOPs in case capabilities are not available. */ -void pa_limit_caps(void) { -} - -void pa_drop_caps(void) { - pa_drop_root(); -} - -pa_bool_t pa_have_caps(void) { - return FALSE; -} +#ifdef HAVE_SYS_CAPABILITY_H + { + cap_t caps; + pa_assert_se(caps = cap_init()); + pa_assert_se(cap_clear(caps) == 0); + pa_assert_se(cap_set_proc(caps) == 0); + pa_assert_se(cap_free(caps) == 0); + } #endif +} diff --git a/src/daemon/caps.h b/src/daemon/caps.h index 94241a9a..5d0ee62e 100644 --- a/src/daemon/caps.h +++ b/src/daemon/caps.h @@ -25,8 +25,5 @@ #include void pa_drop_root(void); -void pa_drop_caps(void); -void pa_limit_caps(void); -pa_bool_t pa_have_caps(void); #endif diff --git a/src/daemon/main.c b/src/daemon/main.c index 8058e122..4b2a1f8b 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -102,7 +102,6 @@ #include "dumpmodules.h" #include "caps.h" #include "ltdl-bind-now.h" -#include "polkit.h" #ifdef HAVE_LIBWRAP /* Only one instance of these variables */ @@ -381,9 +380,7 @@ int main(int argc, char *argv[]) { pa_mainloop *mainloop = NULL; char *s; int r = 0, retval = 1, d = 0; - pa_bool_t suid_root, real_root; pa_bool_t valid_pid_file = FALSE; - gid_t gid = (gid_t) -1; pa_bool_t ltdl_init = FALSE; int passed_fd = -1; const char *e; @@ -426,30 +423,6 @@ int main(int argc, char *argv[]) { } #endif -#ifdef HAVE_GETUID - real_root = getuid() == 0; - suid_root = !real_root && geteuid() == 0; -#else - real_root = FALSE; - suid_root = FALSE; -#endif - - if (!real_root) { - /* Drop all capabilities except CAP_SYS_NICE */ - pa_limit_caps(); - - /* Drop privileges, but keep CAP_SYS_NICE */ - pa_drop_root(); - - /* After dropping root, the effective set is reset, hence, - * let's raise it again */ - pa_limit_caps(); - - /* When capabilities are not supported we will not be able to - * acquire RT sched anymore. But yes, that's the way it is. It - * is just too risky tun let PA run as root all the time. */ - } - if ((e = getenv("PULSE_PASSED_FD"))) { passed_fd = atoi(e); @@ -457,15 +430,14 @@ int main(int argc, char *argv[]) { passed_fd = -1; } + /* We might be autospawned, in which case have no idea in which + * context we have been started. Let's cleanup our execution + * context as good as possible */ + pa_drop_root(); pa_close_all(passed_fd, -1); - pa_reset_sigs(-1); pa_unblock_sigs(-1); - /* At this point, we are a normal user, possibly with CAP_NICE if - * we were started SUID. If we are started as normal root, than we - * still are normal root. */ - setlocale(LC_ALL, ""); pa_init_i18n(); @@ -490,150 +462,6 @@ int main(int argc, char *argv[]) { pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET); pa_log_set_show_backtrace(conf->log_backtrace); - pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root)); - - if (!real_root && pa_have_caps()) { -#ifdef HAVE_SYS_RESOURCE_H - struct rlimit rl; -#endif - pa_bool_t allow_high_priority = FALSE, allow_realtime = FALSE; - - /* Let's better not enable high prio or RT by default */ - - if (conf->high_priority && !allow_high_priority) { - if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) { - pa_log_info(_("We're in the group '%s', allowing high-priority scheduling."), PA_REALTIME_GROUP); - allow_high_priority = TRUE; - } - } - - if (conf->realtime_scheduling && !allow_realtime) { - if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) { - pa_log_info(_("We're in the group '%s', allowing real-time scheduling."), PA_REALTIME_GROUP); - allow_realtime = TRUE; - } - } - -#ifdef HAVE_POLKIT - if (conf->high_priority && !allow_high_priority) { - if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) { - pa_log_info(_("PolicyKit grants us acquire-high-priority privilege.")); - allow_high_priority = TRUE; - } else - pa_log_info(_("PolicyKit refuses acquire-high-priority privilege.")); - } - - if (conf->realtime_scheduling && !allow_realtime) { - if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) { - pa_log_info(_("PolicyKit grants us acquire-real-time privilege.")); - allow_realtime = TRUE; - } else - pa_log_info(_("PolicyKit refuses acquire-real-time privilege.")); - } -#endif - - if (!allow_high_priority && !allow_realtime) { - - /* OK, there's no further need to keep CAP_NICE. Hence - * let's give it up early */ - - pa_drop_caps(); - } - -#ifdef RLIMIT_RTPRIO - if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) - if (rl.rlim_cur > 0) { - pa_log_info("RLIMIT_RTPRIO is set to %u, allowing real-time scheduling.", (unsigned) rl.rlim_cur); - allow_realtime = TRUE; - } -#endif -#ifdef RLIMIT_NICE - if (getrlimit(RLIMIT_NICE, &rl) >= 0) - if (rl.rlim_cur > 20 ) { - pa_log_info("RLIMIT_NICE is set to %u, allowing high-priority scheduling.", (unsigned) rl.rlim_cur); - allow_high_priority = TRUE; - } -#endif - - if ((conf->high_priority && !allow_high_priority) || - (conf->realtime_scheduling && !allow_realtime)) - pa_log_info(_("Called SUID root and real-time and/or high-priority scheduling was requested in the configuration. However, we lack the necessary privileges:\n" - "We are not in group '%s', PolicyKit refuse to grant us the requested privileges and we have no increase RLIMIT_NICE/RLIMIT_RTPRIO resource limits.\n" - "For enabling real-time/high-priority scheduling please acquire the appropriate PolicyKit privileges, or become a member of '%s', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."), - PA_REALTIME_GROUP, PA_REALTIME_GROUP); - - - if (!allow_realtime) - conf->realtime_scheduling = FALSE; - - if (!allow_high_priority) - conf->high_priority = FALSE; - } - -#ifdef HAVE_SYS_RESOURCE_H - /* Reset resource limits. If we are run as root (for system mode) - * this might end up increasing the limits, which is intended - * behaviour. For all other cases, i.e. started as normal user, or - * SUID root at this point we should have no CAP_SYS_RESOURCE and - * increasing the limits thus should fail. Which is, too, intended - * behaviour */ - - set_all_rlimits(conf); -#endif - - if (conf->high_priority && !pa_can_high_priority()) { - pa_log_info(_("High-priority scheduling enabled in configuration but not allowed by policy.")); - conf->high_priority = FALSE; - } - - if (conf->high_priority && (conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START)) - pa_raise_priority(conf->nice_level); - - pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority())); - - if (!real_root && pa_have_caps()) { - pa_bool_t drop; - - drop = (conf->cmd != PA_CMD_DAEMON && conf->cmd != PA_CMD_START) || !conf->realtime_scheduling; - -#ifdef RLIMIT_RTPRIO - if (!drop) { - struct rlimit rl; - /* At this point we still have CAP_NICE if we were loaded - * SUID root. If possible let's acquire RLIMIT_RTPRIO - * instead and give CAP_NICE up. */ - - if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) { - - if (rl.rlim_cur >= 9) - drop = TRUE; - else { - rl.rlim_max = rl.rlim_cur = 9; - - if (setrlimit(RLIMIT_RTPRIO, &rl) >= 0) { - pa_log_info(_("Successfully increased RLIMIT_RTPRIO")); - drop = TRUE; - } else - pa_log_warn(_("RLIMIT_RTPRIO failed: %s"), pa_cstrerror(errno)); - } - } - } -#endif - - if (drop) { - pa_log_info(_("Giving up CAP_NICE")); - pa_drop_caps(); - suid_root = FALSE; - } - } - - if (conf->realtime_scheduling && !pa_can_realtime()) { - pa_log_info(_("Real-time scheduling enabled in configuration but not allowed by policy.")); - conf->realtime_scheduling = FALSE; - } - - pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority())); - LTDL_SET_PRELOADED_SYMBOLS(); pa_ltdl_init(); ltdl_init = TRUE; @@ -718,9 +546,9 @@ int main(int argc, char *argv[]) { pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START); } - if (real_root && !conf->system_instance) + if (getuid() == 0 && !conf->system_instance) pa_log_warn(_("This program is not intended to be run as root (unless --system is specified).")); - else if (!real_root && conf->system_instance) { + else if (getuid() != 0 && conf->system_instance) { pa_log(_("Root privileges required.")); goto finish; } @@ -866,6 +694,13 @@ int main(int argc, char *argv[]) { pa_assert_se(chdir("/") == 0); umask(0022); +#ifdef HAVE_SYS_RESOURCE_H + set_all_rlimits(conf); +#endif + pa_rtclock_hrtimer_enable(); + + pa_raise_priority(conf->nice_level); + if (conf->system_instance) if (change_user() < 0) goto finish; @@ -914,8 +749,8 @@ int main(int argc, char *argv[]) { pa_xfree(s); if ((s = pa_session_id())) { - pa_log_info(_("Session ID is %s."), s); - pa_xfree(s); + pa_log_info(_("Session ID is %s."), s); + pa_xfree(s); } if (!(s = pa_get_runtime_dir())) @@ -962,8 +797,6 @@ int main(int argc, char *argv[]) { else pa_log_info(_("Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!")); - pa_rtclock_hrtimer_enable(); - #ifdef SIGRTMIN /* Valgrind uses SIGRTMAX. To easy debugging we don't use it here */ pa_rtsig_configure(SIGRTMIN, SIGRTMAX-1); -- cgit From 5dcdd5e3583f32c8bffb5a1892e318747070f5d8 Mon Sep 17 00:00:00 2001 From: Marc-AndrĂ© Lureau Date: Sat, 4 Apr 2009 22:56:38 +0300 Subject: perl -p -i -e 's/pa_rtclock_usec/pa_rtclock_now/g' `find . -name '*.[ch]'` --- src/daemon/cpulimit.c | 4 ++-- src/modules/alsa/alsa-sink.c | 12 ++++++------ src/modules/alsa/alsa-source.c | 12 ++++++------ src/modules/bluetooth/module-bluetooth-device.c | 18 +++++++++--------- src/modules/module-combine.c | 12 ++++++------ src/modules/module-esound-sink.c | 10 +++++----- src/modules/module-null-sink.c | 8 ++++---- src/modules/module-sine-source.c | 8 ++++---- src/modules/module-solaris.c | 2 +- src/modules/module-tunnel.c | 12 ++++++------ src/modules/raop/module-raop-sink.c | 10 +++++----- src/modules/rtp/module-rtp-recv.c | 2 +- src/pulse/stream.c | 8 ++++---- src/pulsecore/log.c | 2 +- src/pulsecore/ratelimit.c | 2 +- src/pulsecore/rtclock.c | 2 +- src/pulsecore/rtclock.h | 2 +- src/pulsecore/rtpoll.c | 6 +++--- 18 files changed, 66 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c index 45d6a0fb..e9cefdcc 100644 --- a/src/daemon/cpulimit.c +++ b/src/daemon/cpulimit.c @@ -125,7 +125,7 @@ static void signal_handler(int sig) { char t[256]; #endif - now = pa_rtclock_usec(); + now = pa_rtclock_now(); elapsed = now - last_time; #ifdef PRINT_CPU_LOAD @@ -184,7 +184,7 @@ int pa_cpu_limit_init(pa_mainloop_api *m) { pa_assert(the_pipe[1] == -1); pa_assert(!installed); - last_time = pa_rtclock_usec(); + last_time = pa_rtclock_now(); /* Prepare the main loop pipe */ if (pipe(the_pipe) < 0) { diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 281371f7..37ebe541 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -707,7 +707,7 @@ static void update_smoother(struct userdata *u) { /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */ if (now1 <= 0) - now1 = pa_rtclock_usec(); + now1 = pa_rtclock_now(); now2 = pa_bytes_to_usec((uint64_t) position, &u->sink->sample_spec); @@ -721,7 +721,7 @@ static pa_usec_t sink_get_latency(struct userdata *u) { pa_assert(u); - now1 = pa_rtclock_usec(); + now1 = pa_rtclock_now(); now2 = pa_smoother_get(u->smoother, now1); delay = (int64_t) pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - (int64_t) now2; @@ -752,7 +752,7 @@ static int suspend(struct userdata *u) { pa_assert(u); pa_assert(u->pcm_handle); - pa_smoother_pause(u->smoother, pa_rtclock_usec()); + pa_smoother_pause(u->smoother, pa_rtclock_now()); /* Let's suspend -- we don't call snd_pcm_drain() here since that might * take awfully long with our long buffer sizes today. */ @@ -1246,7 +1246,7 @@ static void thread_func(void *userdata) { pa_log_info("Starting playback."); snd_pcm_start(u->pcm_handle); - pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE); + pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE); } update_smoother(u); @@ -1275,7 +1275,7 @@ static void thread_func(void *userdata) { /* Convert from the sound card time domain to the * system time domain */ - cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec); + cusec = pa_smoother_translate(u->smoother, pa_rtclock_now(), sleep_usec); /* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */ @@ -1581,7 +1581,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca TRUE, TRUE, 5, - pa_rtclock_usec(), + pa_rtclock_now(), TRUE); dev_id = pa_modargs_get_value( diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 4be27eb0..32510654 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -669,7 +669,7 @@ static void update_smoother(struct userdata *u) { /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */ if (now1 <= 0) - now1 = pa_rtclock_usec(); + now1 = pa_rtclock_now(); now2 = pa_bytes_to_usec(position, &u->source->sample_spec); @@ -682,7 +682,7 @@ static pa_usec_t source_get_latency(struct userdata *u) { pa_assert(u); - now1 = pa_rtclock_usec(); + now1 = pa_rtclock_now(); now2 = pa_smoother_get(u->smoother, now1); delay = (int64_t) now2 - (int64_t) pa_bytes_to_usec(u->read_count, &u->source->sample_spec); @@ -707,7 +707,7 @@ static int suspend(struct userdata *u) { pa_assert(u); pa_assert(u->pcm_handle); - pa_smoother_pause(u->smoother, pa_rtclock_usec()); + pa_smoother_pause(u->smoother, pa_rtclock_now()); /* Let's suspend */ snd_pcm_close(u->pcm_handle); @@ -833,7 +833,7 @@ static int unsuspend(struct userdata *u) { /* FIXME: We need to reload the volume somehow */ snd_pcm_start(u->pcm_handle); - pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE); + pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE); pa_log_info("Resumed successfully..."); @@ -1131,7 +1131,7 @@ static void thread_func(void *userdata) { /* Convert from the sound card time domain to the * system time domain */ - cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec); + cusec = pa_smoother_translate(u->smoother, pa_rtclock_now(), sleep_usec); /* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */ @@ -1431,7 +1431,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p TRUE, TRUE, 5, - pa_rtclock_usec(), + pa_rtclock_now(), FALSE); dev_id = pa_modargs_get_value( diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 6bcd0b80..e0d4e481 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -773,7 +773,7 @@ static int start_stream_fd(struct userdata *u) { TRUE, TRUE, 10, - pa_rtclock_usec(), + pa_rtclock_now(), TRUE); return 0; @@ -867,14 +867,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse if (u->read_smoother) { pa_usec_t wi, ri; - ri = pa_smoother_get(u->read_smoother, pa_rtclock_usec()); + ri = pa_smoother_get(u->read_smoother, pa_rtclock_now()); wi = pa_bytes_to_usec(u->write_index + u->block_size, &u->sample_spec); *((pa_usec_t*) data) = wi > ri ? wi - ri : 0; } else { pa_usec_t ri, wi; - ri = pa_rtclock_usec() - u->started_at; + ri = pa_rtclock_now() - u->started_at; wi = pa_bytes_to_usec(u->write_index, &u->sample_spec); *((pa_usec_t*) data) = wi > ri ? wi - ri : 0; @@ -912,7 +912,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off stop_stream_fd(u); if (u->read_smoother) - pa_smoother_pause(u->read_smoother, pa_rtclock_usec()); + pa_smoother_pause(u->read_smoother, pa_rtclock_now()); break; case PA_SOURCE_IDLE: @@ -939,7 +939,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off case PA_SOURCE_MESSAGE_GET_LATENCY: { pa_usec_t wi, ri; - wi = pa_smoother_get(u->read_smoother, pa_rtclock_usec()); + wi = pa_smoother_get(u->read_smoother, pa_rtclock_now()); ri = pa_bytes_to_usec(u->read_index, &u->sample_spec); *((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->fixed_latency; @@ -1086,7 +1086,7 @@ static int hsp_process_push(struct userdata *u) { if (!found_tstamp) { pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); - tstamp = pa_rtclock_usec(); + tstamp = pa_rtclock_now(); } pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); @@ -1309,7 +1309,7 @@ static void thread_func(void *userdata) { /* Hmm, there is no input stream we could synchronize * to. So let's do things by time */ - time_passed = pa_rtclock_usec() - u->started_at; + time_passed = pa_rtclock_now() - u->started_at; audio_sent = pa_bytes_to_usec(u->write_index, &u->sample_spec); if (audio_sent <= time_passed) { @@ -1341,7 +1341,7 @@ static void thread_func(void *userdata) { int n_written; if (u->write_index <= 0) - u->started_at = pa_rtclock_usec(); + u->started_at = pa_rtclock_now(); if (u->profile == PROFILE_A2DP) { if ((n_written = a2dp_process_render(u)) < 0) @@ -1361,7 +1361,7 @@ static void thread_func(void *userdata) { /* Hmm, there is no input stream we could synchronize * to. So let's estimate when we need to wake up the latest */ - time_passed = pa_rtclock_usec() - u->started_at; + time_passed = pa_rtclock_now() - u->started_at; next_write_at = pa_bytes_to_usec(u->write_index, &u->sample_spec); sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0; diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 725faa0c..69dc530b 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -282,7 +282,7 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); - u->thread_info.timestamp = pa_rtclock_usec(); + u->thread_info.timestamp = pa_rtclock_now(); u->thread_info.in_null_mode = FALSE; for (;;) { @@ -296,7 +296,7 @@ static void thread_func(void *userdata) { if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && !u->thread_info.active_outputs) { pa_usec_t now; - now = pa_rtclock_usec(); + now = pa_rtclock_now(); if (!u->thread_info.in_null_mode || u->thread_info.timestamp <= now) process_render_null(u, now); @@ -664,16 +664,16 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_atomic_store(&u->thread_info.running, PA_PTR_TO_UINT(data) == PA_SINK_RUNNING); if (PA_PTR_TO_UINT(data) == PA_SINK_SUSPENDED) - pa_smoother_pause(u->thread_info.smoother, pa_rtclock_usec()); + pa_smoother_pause(u->thread_info.smoother, pa_rtclock_now()); else - pa_smoother_resume(u->thread_info.smoother, pa_rtclock_usec(), TRUE); + pa_smoother_resume(u->thread_info.smoother, pa_rtclock_now(), TRUE); break; case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t x, y, c, *delay = data; - x = pa_rtclock_usec(); + x = pa_rtclock_now(); y = pa_smoother_get(u->thread_info.smoother, x); c = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec); @@ -730,7 +730,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case SINK_MESSAGE_UPDATE_LATENCY: { pa_usec_t x, y, latency = (pa_usec_t) offset; - x = pa_rtclock_usec(); + x = pa_rtclock_now(); y = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec); if (y > latency) diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 8cb25c51..d631da03 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -145,14 +145,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_SUSPENDED: pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); - pa_smoother_pause(u->smoother, pa_rtclock_usec()); + pa_smoother_pause(u->smoother, pa_rtclock_now()); break; case PA_SINK_IDLE: case PA_SINK_RUNNING: if (u->sink->thread_info.state == PA_SINK_SUSPENDED) - pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE); + pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE); break; @@ -167,7 +167,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t w, r; - r = pa_smoother_get(u->smoother, pa_rtclock_usec()); + r = pa_smoother_get(u->smoother, pa_rtclock_now()); w = pa_bytes_to_usec((uint64_t) u->offset + u->memchunk.length, &u->sink->sample_spec); *((pa_usec_t*) data) = w > r ? w - r : 0; @@ -202,7 +202,7 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); - pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); + pa_smoother_set_time_offset(u->smoother, pa_rtclock_now()); for (;;) { int ret; @@ -295,7 +295,7 @@ static void thread_func(void *userdata) { else usec = 0; - pa_smoother_put(u->smoother, pa_rtclock_usec(), usec); + pa_smoother_put(u->smoother, pa_rtclock_now(), usec); } /* Hmm, nothing to do. Let's sleep */ diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index 30a99ca7..8981d52d 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -101,14 +101,14 @@ static int sink_process_msg( case PA_SINK_MESSAGE_SET_STATE: if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING) - u->timestamp = pa_rtclock_usec(); + u->timestamp = pa_rtclock_now(); break; case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t now; - now = pa_rtclock_usec(); + now = pa_rtclock_now(); *((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0ULL; return 0; @@ -210,7 +210,7 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); - u->timestamp = pa_rtclock_usec(); + u->timestamp = pa_rtclock_now(); for (;;) { int ret; @@ -219,7 +219,7 @@ static void thread_func(void *userdata) { if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { pa_usec_t now; - now = pa_rtclock_usec(); + now = pa_rtclock_now(); if (u->sink->thread_info.rewind_requested) { if (u->sink->thread_info.rewind_nbytes > 0) diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c index 14a04e47..9be4e195 100644 --- a/src/modules/module-sine-source.c +++ b/src/modules/module-sine-source.c @@ -101,14 +101,14 @@ static int source_process_msg( case PA_SOURCE_MESSAGE_SET_STATE: if (PA_PTR_TO_UINT(data) == PA_SOURCE_RUNNING) - u->timestamp = pa_rtclock_usec(); + u->timestamp = pa_rtclock_now(); break; case PA_SOURCE_MESSAGE_GET_LATENCY: { pa_usec_t now, left_to_fill; - now = pa_rtclock_usec(); + now = pa_rtclock_now(); left_to_fill = u->timestamp > now ? u->timestamp - now : 0ULL; *((pa_usec_t*) data) = u->block_usec > left_to_fill ? u->block_usec - left_to_fill : 0ULL; @@ -168,7 +168,7 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); - u->timestamp = pa_rtclock_usec(); + u->timestamp = pa_rtclock_now(); for (;;) { int ret; @@ -176,7 +176,7 @@ static void thread_func(void *userdata) { if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { pa_usec_t now; - now = pa_rtclock_usec(); + now = pa_rtclock_now(); if (u->timestamp <= now) process_render(u, now); diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c index 5cfa97a7..08fe6f58 100644 --- a/src/modules/module-solaris.c +++ b/src/modules/module-solaris.c @@ -641,7 +641,7 @@ static void thread_func(void *userdata) { * Since we cannot modify the size of the output buffer we fake it * by not filling it more than u->buffer_size. */ - xtime0 = pa_rtclock_usec(); + xtime0 = pa_rtclock_now(); buffered_bytes = get_playback_buffered_bytes(u); if (buffered_bytes >= (uint64_t)u->buffer_size) break; diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index c493d9bb..60498ad7 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -395,7 +395,7 @@ static void check_smoother_status(struct userdata *u, pa_bool_t past) { pa_assert(u); - x = pa_rtclock_usec(); + x = pa_rtclock_now(); /* Correct by the time the requested issued needs to travel to the * other side. This is a valid thread-safe access, because the @@ -500,7 +500,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_usec_t yl, yr, *usec = data; yl = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec); - yr = pa_smoother_get(u->smoother, pa_rtclock_usec()); + yr = pa_smoother_get(u->smoother, pa_rtclock_now()); *usec = yl > yr ? yl - yr : 0; return 0; @@ -533,7 +533,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse else y = 0; - pa_smoother_put(u->smoother, pa_rtclock_usec(), y); + pa_smoother_put(u->smoother, pa_rtclock_now(), y); /* We can access this freely here, since the main thread is waiting for us */ u->thread_transport_usec = u->transport_usec; @@ -607,7 +607,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off pa_usec_t yr, yl, *usec = data; yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SOURCE(o)->sample_spec); - yr = pa_smoother_get(u->smoother, pa_rtclock_usec()); + yr = pa_smoother_get(u->smoother, pa_rtclock_now()); *usec = yr > yl ? yr - yl : 0; return 0; @@ -633,7 +633,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec); y += (pa_usec_t) offset; - pa_smoother_put(u->smoother, pa_rtclock_usec(), y); + pa_smoother_put(u->smoother, pa_rtclock_now(), y); /* We can access this freely here, since the main thread is waiting for us */ u->thread_transport_usec = u->transport_usec; @@ -1825,7 +1825,7 @@ int pa__init(pa_module*m) { TRUE, TRUE, 10, - pa_rtclock_usec(), + pa_rtclock_now(), FALSE); u->ctag = 1; u->device_index = u->channel = PA_INVALID_INDEX; diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c index 54de42c2..0e6b9d42 100644 --- a/src/modules/raop/module-raop-sink.c +++ b/src/modules/raop/module-raop-sink.c @@ -181,7 +181,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_SUSPENDED: pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); - pa_smoother_pause(u->smoother, pa_rtclock_usec()); + pa_smoother_pause(u->smoother, pa_rtclock_now()); /* Issue a FLUSH if we are connected */ if (u->fd >= 0) { @@ -193,7 +193,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_RUNNING: if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { - pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE); + pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE); /* The connection can be closed when idle, so check to see if we need to reestablish it */ @@ -216,7 +216,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t w, r; - r = pa_smoother_get(u->smoother, pa_rtclock_usec()); + r = pa_smoother_get(u->smoother, pa_rtclock_now()); w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec); *((pa_usec_t*) data) = w > r ? w - r : 0; @@ -325,7 +325,7 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); - pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); + pa_smoother_set_time_offset(u->smoother, pa_rtclock_now()); /* Create a chunk of memory that is our encoded silence sample. */ pa_memchunk_reset(&silence); @@ -465,7 +465,7 @@ static void thread_func(void *userdata) { else usec = 0; - pa_smoother_put(u->smoother, pa_rtclock_usec(), usec); + pa_smoother_put(u->smoother, pa_rtclock_now(), usec); } /* Hmm, nothing to do. Let's sleep */ diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index b86923fb..5a935922 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -193,7 +193,7 @@ static void sink_input_suspend_within_thread(pa_sink_input* i, pa_bool_t b) { pa_assert_se(s = i->userdata); if (b) { - pa_smoother_pause(s->smoother, pa_rtclock_usec()); + pa_smoother_pause(s->smoother, pa_rtclock_now()); pa_memblockq_flush_read(s->memblockq); } else s->first_packet = FALSE; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 339a89e5..8df8a5e2 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -373,7 +373,7 @@ static void check_smoother_status(pa_stream *s, pa_bool_t aposteriori, pa_bool_t if (!s->smoother) return; - x = pa_rtclock_usec(); + x = pa_rtclock_now(); if (s->timing_info_valid) { if (aposteriori) @@ -1057,7 +1057,7 @@ static int create_stream( if (flags & PA_STREAM_INTERPOLATE_TIMING) { pa_usec_t x; - x = pa_rtclock_usec(); + x = pa_rtclock_now(); pa_assert(!s->smoother); s->smoother = pa_smoother_new( @@ -1594,7 +1594,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, if (o->stream->smoother) { pa_usec_t u, x; - u = x = pa_rtclock_usec() - i->transport_usec; + u = x = pa_rtclock_now() - i->transport_usec; if (o->stream->direction == PA_STREAM_PLAYBACK && o->context->version >= 13) { pa_usec_t su; @@ -2103,7 +2103,7 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA); if (s->smoother) - usec = pa_smoother_get(s->smoother, pa_rtclock_usec()); + usec = pa_smoother_get(s->smoother, pa_rtclock_now()); else usec = calc_time(s, FALSE); diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index 15d192d6..acff981b 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -294,7 +294,7 @@ void pa_log_levelv_meta( static pa_usec_t start, last; pa_usec_t u, a, r; - u = pa_rtclock_usec(); + u = pa_rtclock_now(); PA_ONCE_BEGIN { start = u; diff --git a/src/pulsecore/ratelimit.c b/src/pulsecore/ratelimit.c index e913ca19..f00587af 100644 --- a/src/pulsecore/ratelimit.c +++ b/src/pulsecore/ratelimit.c @@ -38,7 +38,7 @@ pa_bool_t pa_ratelimit_test(pa_ratelimit *r) { pa_usec_t now; pa_mutex *m; - now = pa_rtclock_usec(); + now = pa_rtclock_now(); m = pa_static_mutex_get(&mutex, FALSE, FALSE); pa_mutex_lock(m); diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c index 65c826a6..53c0258c 100644 --- a/src/pulsecore/rtclock.c +++ b/src/pulsecore/rtclock.c @@ -122,7 +122,7 @@ void pa_rtclock_hrtimer_enable(void) { #endif } -pa_usec_t pa_rtclock_usec(void) { +pa_usec_t pa_rtclock_now(void) { struct timeval tv; return pa_timeval_load(pa_rtclock_get(&tv)); diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h index 03cc1c72..b8a588c0 100644 --- a/src/pulsecore/rtclock.h +++ b/src/pulsecore/rtclock.h @@ -31,7 +31,7 @@ struct timeval; struct timeval *pa_rtclock_get(struct timeval *ts); -pa_usec_t pa_rtclock_usec(void); +pa_usec_t pa_rtclock_now(void); pa_usec_t pa_rtclock_age(const struct timeval *tv); pa_bool_t pa_rtclock_hrtimer(void); diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index aa8ca321..006b93f6 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -160,7 +160,7 @@ pa_rtpoll *pa_rtpoll_new(void) { PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items); #ifdef DEBUG_TIMING - p->timestamp = pa_rtclock_usec(); + p->timestamp = pa_rtclock_now(); p->slept = p->awake = 0; #endif @@ -394,7 +394,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) { #ifdef DEBUG_TIMING { - pa_usec_t now = pa_rtclock_usec(); + pa_usec_t now = pa_rtclock_now(); p->awake = now - p->timestamp; p->timestamp = now; } @@ -421,7 +421,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) { #ifdef DEBUG_TIMING { - pa_usec_t now = pa_rtclock_usec(); + pa_usec_t now = pa_rtclock_now(); p->slept = now - p->timestamp; p->timestamp = now; -- cgit From a62db279b71f8a715c1559fbc4bb1c037159fd14 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jun 2009 18:21:35 +0200 Subject: daemon: enable real-time by default --- src/daemon/daemon-conf.c | 2 +- src/daemon/daemon.conf.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index 664e4fde..9010f2f6 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -60,7 +60,7 @@ static const pa_daemon_conf default_conf = { .fail = TRUE, .high_priority = TRUE, .nice_level = -11, - .realtime_scheduling = FALSE, + .realtime_scheduling = TRUE, .realtime_priority = 5, /* Half of JACK's default rtprio */ .disallow_module_loading = FALSE, .disallow_exit = FALSE, diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index d119716d..6931359c 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -33,7 +33,7 @@ ; high-priority = yes ; nice-level = -11 -; realtime-scheduling = no +; realtime-scheduling = yes ; realtime-priority = 5 ; exit-idle-time = 20 -- cgit From 32e2cd6d3216f780c4cffed0f8eb3c30f2c8d732 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jun 2009 21:00:06 +0200 Subject: core: get rid of rt sig/timer handling since modern Linux' ppooll() is finally fixed for granularity --- src/Makefile.am | 1 - src/daemon/main.c | 6 - src/modules/alsa/alsa-sink.c | 1 - src/modules/alsa/alsa-source.c | 1 - src/modules/bluetooth/module-bluetooth-device.c | 1 - src/modules/jack/module-jack-sink.c | 1 - src/modules/jack/module-jack-source.c | 1 - src/modules/module-combine.c | 1 - src/modules/module-esound-sink.c | 1 - src/modules/module-null-sink.c | 1 - src/modules/module-pipe-sink.c | 1 - src/modules/module-pipe-source.c | 1 - src/modules/module-sine-source.c | 1 - src/modules/module-tunnel.c | 1 - src/modules/oss/module-oss.c | 1 - src/modules/raop/module-raop-sink.c | 1 - src/pulsecore/rtpoll.c | 176 +----------------------- src/pulsecore/rtpoll.h | 3 - src/pulsecore/rtsig.c | 131 ------------------ src/pulsecore/rtsig.h | 39 ------ src/tests/rtpoll-test.c | 6 - 21 files changed, 4 insertions(+), 372 deletions(-) delete mode 100644 src/pulsecore/rtsig.c delete mode 100644 src/pulsecore/rtsig.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 5efe1ac8..d5fbf275 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -811,7 +811,6 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/play-memchunk.c pulsecore/play-memchunk.h \ pulsecore/resampler.c pulsecore/resampler.h \ pulsecore/rtpoll.c pulsecore/rtpoll.h \ - pulsecore/rtsig.c pulsecore/rtsig.h \ pulsecore/sample-util.c pulsecore/sample-util.h \ pulsecore/sconv-s16be.c pulsecore/sconv-s16be.h \ pulsecore/sconv-s16le.c pulsecore/sconv-s16le.h \ diff --git a/src/daemon/main.c b/src/daemon/main.c index 4b2a1f8b..2ecd6c2f 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -84,7 +84,6 @@ #include #include #include -#include #include #include #include @@ -797,11 +796,6 @@ int main(int argc, char *argv[]) { else pa_log_info(_("Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!")); -#ifdef SIGRTMIN - /* Valgrind uses SIGRTMAX. To easy debugging we don't use it here */ - pa_rtsig_configure(SIGRTMIN, SIGRTMAX-1); -#endif - if (conf->lock_memory) { #ifdef HAVE_SYS_MMAN_H if (mlockall(MCL_FUTURE) < 0) diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 281371f7..c39a898f 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1212,7 +1212,6 @@ static void thread_func(void *userdata) { pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { int ret; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 4be27eb0..755ceae2 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1094,7 +1094,6 @@ static void thread_func(void *userdata) { pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { int ret; diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 6bcd0b80..0f1e78f2 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1265,7 +1265,6 @@ static void thread_func(void *userdata) { goto fail; pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { struct pollfd *pollfd; diff --git a/src/modules/jack/module-jack-sink.c b/src/modules/jack/module-jack-sink.c index 290038e7..fc976fa7 100644 --- a/src/modules/jack/module-jack-sink.c +++ b/src/modules/jack/module-jack-sink.c @@ -225,7 +225,6 @@ static void thread_func(void *userdata) { pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { int ret; diff --git a/src/modules/jack/module-jack-source.c b/src/modules/jack/module-jack-source.c index ef89a98e..a898e0e5 100644 --- a/src/modules/jack/module-jack-source.c +++ b/src/modules/jack/module-jack-source.c @@ -196,7 +196,6 @@ static void thread_func(void *userdata) { pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { int ret; diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 725faa0c..cd3118d7 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -280,7 +280,6 @@ static void thread_func(void *userdata) { pa_make_realtime(u->core->realtime_priority+1); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); u->thread_info.timestamp = pa_rtclock_usec(); u->thread_info.in_null_mode = FALSE; diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 8cb25c51..48a9c794 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -200,7 +200,6 @@ static void thread_func(void *userdata) { pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index 30a99ca7..c4753c9d 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -208,7 +208,6 @@ static void thread_func(void *userdata) { pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); u->timestamp = pa_rtclock_usec(); diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index 5b0f6414..8a7dc846 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -170,7 +170,6 @@ static void thread_func(void *userdata) { pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { struct pollfd *pollfd; diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c index 61c9fc0e..e5609fb5 100644 --- a/src/modules/module-pipe-source.c +++ b/src/modules/module-pipe-source.c @@ -129,7 +129,6 @@ static void thread_func(void *userdata) { pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { int ret; diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c index 14a04e47..8df4d9bb 100644 --- a/src/modules/module-sine-source.c +++ b/src/modules/module-sine-source.c @@ -166,7 +166,6 @@ static void thread_func(void *userdata) { pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); u->timestamp = pa_rtclock_usec(); diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index c493d9bb..eaed5f9a 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -683,7 +683,6 @@ static void thread_func(void *userdata) { pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { int ret; diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c index b1afcfd6..c44b882b 100644 --- a/src/modules/oss/module-oss.c +++ b/src/modules/oss/module-oss.c @@ -889,7 +889,6 @@ static void thread_func(void *userdata) { pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { int ret; diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c index 54de42c2..cab3a82a 100644 --- a/src/modules/raop/module-raop-sink.c +++ b/src/modules/raop/module-raop-sink.c @@ -323,7 +323,6 @@ static void thread_func(void *userdata) { pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index aa8ca321..185b8211 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -30,10 +30,6 @@ #include #include -#ifdef __linux__ -#include -#endif - #ifdef HAVE_POLL_H #include #else @@ -47,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -66,20 +61,9 @@ struct pa_rtpoll { pa_bool_t scan_for_dead:1; pa_bool_t running:1; - pa_bool_t installed:1; pa_bool_t rebuild_needed:1; pa_bool_t quit:1; -#ifdef HAVE_PPOLL - pa_bool_t timer_armed:1; -#ifdef __linux__ - pa_bool_t dont_use_ppoll:1; -#endif - int rtsig; - sigset_t sigset_unblocked; - timer_t timer; -#endif - #ifdef DEBUG_TIMING pa_usec_t timestamp; pa_usec_t slept, awake; @@ -107,52 +91,20 @@ struct pa_rtpoll_item { PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree); -static void signal_handler_noop(int s) { /* write(2, "signal\n", 7); */ } - pa_rtpoll *pa_rtpoll_new(void) { pa_rtpoll *p; p = pa_xnew(pa_rtpoll, 1); -#ifdef HAVE_PPOLL - -#ifdef __linux__ - /* ppoll is broken on Linux < 2.6.16 */ - p->dont_use_ppoll = FALSE; - - { - struct utsname u; - unsigned major, minor, micro; - - pa_assert_se(uname(&u) == 0); - - if (sscanf(u.release, "%u.%u.%u", &major, &minor, µ) != 3 || - (major < 2) || - (major == 2 && minor < 6) || - (major == 2 && minor == 6 && micro < 16)) - - p->dont_use_ppoll = TRUE; - } - -#endif - - p->rtsig = -1; - sigemptyset(&p->sigset_unblocked); - p->timer = (timer_t) -1; - p->timer_armed = FALSE; - -#endif - p->n_pollfd_alloc = 32; p->pollfd = pa_xnew(struct pollfd, p->n_pollfd_alloc); p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc); p->n_pollfd_used = 0; - memset(&p->next_elapse, 0, sizeof(p->next_elapse)); + pa_zero(p->next_elapse); p->timer_enabled = FALSE; p->running = FALSE; - p->installed = FALSE; p->scan_for_dead = FALSE; p->rebuild_needed = FALSE; p->quit = FALSE; @@ -167,46 +119,6 @@ pa_rtpoll *pa_rtpoll_new(void) { return p; } -void pa_rtpoll_install(pa_rtpoll *p) { - pa_assert(p); - pa_assert(!p->installed); - - p->installed = TRUE; - -#ifdef HAVE_PPOLL -# ifdef __linux__ - if (p->dont_use_ppoll) - return; -# endif - - if ((p->rtsig = pa_rtsig_get_for_thread()) < 0) { - pa_log_warn("Failed to reserve POSIX realtime signal."); - return; - } - - pa_log_debug("Acquired POSIX realtime signal %s", pa_sig2str(p->rtsig)); - - { - sigset_t ss; - struct sigaction sa; - - pa_assert_se(sigemptyset(&ss) == 0); - pa_assert_se(sigaddset(&ss, p->rtsig) == 0); - pa_assert_se(pthread_sigmask(SIG_BLOCK, &ss, &p->sigset_unblocked) == 0); - pa_assert_se(sigdelset(&p->sigset_unblocked, p->rtsig) == 0); - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = signal_handler_noop; - pa_assert_se(sigemptyset(&sa.sa_mask) == 0); - - pa_assert_se(sigaction(p->rtsig, &sa, NULL) == 0); - - /* We never reset the signal handler. Why should we? */ - } - -#endif -} - static void rtpoll_rebuild(pa_rtpoll *p) { struct pollfd *e, *t; @@ -250,7 +162,6 @@ static void rtpoll_rebuild(pa_rtpoll *p) { if (ra) p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd)); - } static void rtpoll_item_destroy(pa_rtpoll_item *i) { @@ -279,11 +190,6 @@ void pa_rtpoll_free(pa_rtpoll *p) { pa_xfree(p->pollfd); pa_xfree(p->pollfd2); -#ifdef HAVE_PPOLL - if (p->timer != (timer_t) -1) - timer_delete(p->timer); -#endif - pa_xfree(p); } @@ -321,7 +227,6 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) { pa_assert(p); pa_assert(!p->running); - pa_assert(p->installed); p->running = TRUE; @@ -402,22 +307,15 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) { /* OK, now let's sleep */ #ifdef HAVE_PPOLL - -#ifdef __linux__ - if (!p->dont_use_ppoll) -#endif { struct timespec ts; ts.tv_sec = timeout.tv_sec; ts.tv_nsec = timeout.tv_usec * 1000; - r = ppoll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? &ts : NULL, p->rtsig < 0 ? NULL : &p->sigset_unblocked); + r = ppoll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? &ts : NULL, NULL); } -#ifdef __linux__ - else -#endif - -#endif +#else r = poll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1); +#endif #ifdef DEBUG_TIMING { @@ -472,73 +370,11 @@ finish: return r < 0 ? r : !p->quit; } -static void update_timer(pa_rtpoll *p) { - pa_assert(p); - -#ifdef HAVE_PPOLL - -#ifdef __linux__ - if (p->dont_use_ppoll) - return; -#endif - - if (p->timer == (timer_t) -1) { - struct sigevent se; - - memset(&se, 0, sizeof(se)); - se.sigev_notify = SIGEV_SIGNAL; - se.sigev_signo = p->rtsig; - - if (timer_create(CLOCK_MONOTONIC, &se, &p->timer) < 0) - if (timer_create(CLOCK_REALTIME, &se, &p->timer) < 0) { - pa_log_warn("Failed to allocate POSIX timer: %s", pa_cstrerror(errno)); - p->timer = (timer_t) -1; - } - } - - if (p->timer != (timer_t) -1) { - struct itimerspec its; - struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; - sigset_t ss; - - if (p->timer_armed) { - /* First disarm timer */ - memset(&its, 0, sizeof(its)); - pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0); - - /* Remove a signal that might be waiting in the signal q */ - pa_assert_se(sigemptyset(&ss) == 0); - pa_assert_se(sigaddset(&ss, p->rtsig) == 0); - sigtimedwait(&ss, NULL, &ts); - } - - /* And install the new timer */ - if (p->timer_enabled) { - memset(&its, 0, sizeof(its)); - - its.it_value.tv_sec = p->next_elapse.tv_sec; - its.it_value.tv_nsec = p->next_elapse.tv_usec*1000; - - /* Make sure that 0,0 is not understood as - * "disarming" */ - if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0) - its.it_value.tv_nsec = 1; - pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0); - } - - p->timer_armed = p->timer_enabled; - } - -#endif -} - void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec) { pa_assert(p); pa_timeval_store(&p->next_elapse, usec); p->timer_enabled = TRUE; - - update_timer(p); } void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) { @@ -550,8 +386,6 @@ void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) { pa_rtclock_get(&p->next_elapse); pa_timeval_add(&p->next_elapse, usec); p->timer_enabled = TRUE; - - update_timer(p); } void pa_rtpoll_set_timer_disabled(pa_rtpoll *p) { @@ -559,8 +393,6 @@ void pa_rtpoll_set_timer_disabled(pa_rtpoll *p) { memset(&p->next_elapse, 0, sizeof(p->next_elapse)); p->timer_enabled = FALSE; - - update_timer(p); } pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds) { diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h index 08776ef0..d2d69cad 100644 --- a/src/pulsecore/rtpoll.h +++ b/src/pulsecore/rtpoll.h @@ -62,9 +62,6 @@ typedef enum pa_rtpoll_priority { pa_rtpoll *pa_rtpoll_new(void); void pa_rtpoll_free(pa_rtpoll *p); -/* Install the rtpoll in the current thread */ -void pa_rtpoll_install(pa_rtpoll *p); - /* Sleep on the rtpoll until the time event, or any of the fd events * is triggered. If "wait" is 0 we don't sleep but only update the * struct pollfd. Returns negative on error, positive if the loop diff --git a/src/pulsecore/rtsig.c b/src/pulsecore/rtsig.c deleted file mode 100644 index 4cd6aa8f..00000000 --- a/src/pulsecore/rtsig.c +++ /dev/null @@ -1,131 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - Copyright 2006 Pierre Ossman for Cendio AB - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include -#include -#include -#include -#include - -#include "rtsig.h" - -#ifdef SIGRTMIN - -static void _free_rtsig(void *p) { - pa_rtsig_put(PA_PTR_TO_INT(p)); -} - -PA_STATIC_FLIST_DECLARE(rtsig_flist, pa_make_power_of_two((unsigned) (SIGRTMAX-SIGRTMIN+1)), NULL); -PA_STATIC_TLS_DECLARE(rtsig_tls, _free_rtsig); - -static pa_atomic_t rtsig_current = PA_ATOMIC_INIT(-1); - -static int rtsig_start = -1, rtsig_end = -1; - -int pa_rtsig_get(void) { - void *p; - int sig; - - if ((p = pa_flist_pop(PA_STATIC_FLIST_GET(rtsig_flist)))) - return PA_PTR_TO_INT(p); - - sig = pa_atomic_dec(&rtsig_current); - - pa_assert(sig <= SIGRTMAX); - pa_assert(sig <= rtsig_end); - - if (sig < rtsig_start) { - pa_atomic_inc(&rtsig_current); - return -1; - } - - return sig; -} - -int pa_rtsig_get_for_thread(void) { - int sig; - void *p; - - if ((p = PA_STATIC_TLS_GET(rtsig_tls))) - return PA_PTR_TO_INT(p); - - if ((sig = pa_rtsig_get()) < 0) - return -1; - - PA_STATIC_TLS_SET(rtsig_tls, PA_INT_TO_PTR(sig)); - return sig; -} - -void pa_rtsig_put(int sig) { - pa_assert(sig >= rtsig_start); - pa_assert(sig <= rtsig_end); - - pa_assert_se(pa_flist_push(PA_STATIC_FLIST_GET(rtsig_flist), PA_INT_TO_PTR(sig)) >= 0); -} - -void pa_rtsig_configure(int start, int end) { - int s; - sigset_t ss; - - pa_assert(pa_atomic_load(&rtsig_current) == -1); - - pa_assert(SIGRTMIN <= start); - pa_assert(start <= end); - pa_assert(end <= SIGRTMAX); - - rtsig_start = start; - rtsig_end = end; - - sigemptyset(&ss); - - for (s = rtsig_start; s <= rtsig_end; s++) - pa_assert_se(sigaddset(&ss, s) == 0); - - pa_assert(pthread_sigmask(SIG_BLOCK, &ss, NULL) == 0); - - /* We allocate starting from the end */ - pa_atomic_store(&rtsig_current, rtsig_end); -} - -#else /* SIGRTMIN */ - -int pa_rtsig_get(void) { - return -1; -} - -int pa_rtsig_get_for_thread(void) { - return -1; -} - -void pa_rtsig_put(int sig) { -} - -void pa_rtsig_configure(int start, int end) { -} - -#endif /* SIGRTMIN */ diff --git a/src/pulsecore/rtsig.h b/src/pulsecore/rtsig.h deleted file mode 100644 index e414122d..00000000 --- a/src/pulsecore/rtsig.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef foopulsertsighfoo -#define foopulsertsighfoo - -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* Return the next unused POSIX Realtime signals */ -int pa_rtsig_get(void); - -/* If not called before in the current thread, return the next unused - * rtsig, and install it in a TLS region and give it up automatically - * when the thread shuts down */ -int pa_rtsig_get_for_thread(void); - -/* Give an rtsig back. */ -void pa_rtsig_put(int sig); - -/* Block all RT signals */ -void pa_rtsig_configure(int start, int end); - -#endif diff --git a/src/tests/rtpoll-test.c b/src/tests/rtpoll-test.c index 4ac96446..1706cdfa 100644 --- a/src/tests/rtpoll-test.c +++ b/src/tests/rtpoll-test.c @@ -26,7 +26,6 @@ #include #include -#include static int before(pa_rtpoll_item *i) { pa_log("before"); @@ -47,10 +46,6 @@ int main(int argc, char *argv[]) { pa_rtpoll_item *i, *w; struct pollfd *pollfd; -#ifdef SIGRTMIN - pa_rtsig_configure(SIGRTMIN+10, SIGRTMAX); -#endif - p = pa_rtpoll_new(); i = pa_rtpoll_item_new(p, PA_RTPOLL_EARLY, 1); @@ -64,7 +59,6 @@ int main(int argc, char *argv[]) { w = pa_rtpoll_item_new(p, PA_RTPOLL_NORMAL, 0); pa_rtpoll_item_set_before_callback(w, worker); - pa_rtpoll_install(p); pa_rtpoll_set_timer_relative(p, 10000000); /* 10 s */ pa_rtpoll_run(p, 1); -- cgit From 125c52889626b2ac408ecbcc8ea85575f5808e07 Mon Sep 17 00:00:00 2001 From: Marc-AndrĂ© Lureau Date: Sat, 4 Apr 2009 23:19:53 +0300 Subject: pulse: move pa_rtclock_now in pulsecommon --- src/Makefile.am | 3 +- src/daemon/cpulimit.c | 3 +- src/daemon/main.c | 2 +- src/map-file | 1 + src/modules/alsa/alsa-sink.c | 9 +- src/modules/alsa/alsa-source.c | 9 +- src/modules/alsa/module-alsa-source.c | 2 +- src/modules/bluetooth/module-bluetooth-device.c | 9 +- src/modules/module-combine.c | 3 +- src/modules/module-esound-sink.c | 5 +- src/modules/module-null-sink.c | 3 +- src/modules/module-sine-source.c | 5 +- src/modules/module-tunnel.c | 3 +- src/modules/raop/module-raop-sink.c | 5 +- src/modules/rtp/module-rtp-recv.c | 3 +- src/pulse/rtclock.c | 35 ++++++ src/pulse/rtclock.h | 39 ++++++ src/pulse/stream.c | 3 +- src/pulsecore/core-rtclock.c | 152 +++++++++++++++++++++++ src/pulsecore/core-rtclock.h | 45 +++++++ src/pulsecore/log.c | 3 +- src/pulsecore/ratelimit.c | 3 +- src/pulsecore/rtclock.c | 158 ------------------------ src/pulsecore/rtclock.h | 47 ------- src/pulsecore/rtpoll.c | 2 +- 25 files changed, 317 insertions(+), 235 deletions(-) create mode 100644 src/pulse/rtclock.c create mode 100644 src/pulse/rtclock.h create mode 100644 src/pulsecore/core-rtclock.c create mode 100644 src/pulsecore/core-rtclock.h delete mode 100644 src/pulsecore/rtclock.c delete mode 100644 src/pulsecore/rtclock.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 92453adf..53c21451 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -572,6 +572,7 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/authkey.c pulsecore/authkey.h \ pulsecore/conf-parser.c pulsecore/conf-parser.h \ pulsecore/core-error.c pulsecore/core-error.h \ + pulsecore/core-rtclock.c pulsecore/core-rtclock.h \ pulsecore/core-util.c pulsecore/core-util.h \ pulsecore/creds.h \ pulsecore/dynarray.c pulsecore/dynarray.h \ @@ -610,7 +611,6 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/queue.c pulsecore/queue.h \ pulsecore/random.c pulsecore/random.h \ pulsecore/refcnt.h \ - pulsecore/rtclock.c pulsecore/rtclock.h \ pulsecore/shm.c pulsecore/shm.h \ pulsecore/bitset.c pulsecore/bitset.h \ pulsecore/socket-client.c pulsecore/socket-client.h \ @@ -738,6 +738,7 @@ libpulse_la_SOURCES = \ pulse/operation.c pulse/operation.h \ pulse/proplist.c pulse/proplist.h \ pulse/pulseaudio.h \ + pulse/rtclock.c pulse/rtclock.h \ pulse/sample.c pulse/sample.h \ pulse/scache.c pulse/scache.h \ pulse/stream.c pulse/stream.h \ diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c index e9cefdcc..c2877ecf 100644 --- a/src/daemon/cpulimit.c +++ b/src/daemon/cpulimit.c @@ -24,13 +24,14 @@ #endif #include +#include #include +#include #include #include #include #include -#include #include "cpulimit.h" diff --git a/src/daemon/main.c b/src/daemon/main.c index 8058e122..083e4eae 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -73,6 +73,7 @@ #include #include #include +#include #include #include #include @@ -85,7 +86,6 @@ #include #include #include -#include #include #include #include diff --git a/src/map-file b/src/map-file index a2cc6c5d..e96eac51 100644 --- a/src/map-file +++ b/src/map-file @@ -195,6 +195,7 @@ pa_proplist_to_string_sep; pa_proplist_unset; pa_proplist_unset_many; pa_proplist_update; +pa_rtclock_now; pa_sample_format_is_be; pa_sample_format_is_le; pa_sample_format_to_string; diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 37ebe541..9116a69f 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -32,16 +32,18 @@ #include #endif -#include -#include -#include #include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -50,7 +52,6 @@ #include #include #include -#include #include #include diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 32510654..ede9306f 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -28,10 +28,11 @@ #include -#include -#include -#include #include +#include +#include +#include +#include #include #include @@ -39,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +50,6 @@ #include #include #include -#include #include diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c index de8bcd0b..23da4185 100644 --- a/src/modules/alsa/module-alsa-source.c +++ b/src/modules/alsa/module-alsa-source.c @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -51,7 +52,6 @@ #include #include #include -#include #include "alsa-util.h" #include "alsa-source.h" diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index e0d4e481..68ac3acc 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -30,13 +30,15 @@ #include #include -#include -#include -#include #include +#include +#include +#include +#include #include #include +#include #include #include #include @@ -44,7 +46,6 @@ #include #include #include -#include #include #include diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 69dc530b..6ca7cd99 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -43,7 +45,6 @@ #include #include #include -#include #include #include diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index d631da03..2df8a0e8 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -41,13 +41,15 @@ #include #endif -#include +#include #include +#include #include #include #include #include +#include #include #include #include @@ -57,7 +59,6 @@ #include #include #include -#include #include #include "module-esound-sink-symdef.h" diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index 8981d52d..2669776d 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -32,12 +32,14 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -45,7 +47,6 @@ #include #include #include -#include #include "module-null-sink-symdef.h" diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c index 9be4e195..33be66f5 100644 --- a/src/modules/module-sine-source.c +++ b/src/modules/module-sine-source.c @@ -34,19 +34,20 @@ #include #include -#include +#include #include +#include #include #include #include +#include #include #include #include #include #include #include -#include #include "module-sine-source-symdef.h" diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 60498ad7..ec4808f0 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -50,7 +51,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c index 0e6b9d42..c226b0c3 100644 --- a/src/modules/raop/module-raop-sink.c +++ b/src/modules/raop/module-raop-sink.c @@ -42,13 +42,15 @@ #include #endif -#include +#include #include +#include #include #include #include #include +#include #include #include #include @@ -57,7 +59,6 @@ #include #include #include -#include #include #include "module-raop-sink-symdef.h" diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index 5a935922..84cf07de 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -43,13 +44,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include diff --git a/src/pulse/rtclock.c b/src/pulse/rtclock.c new file mode 100644 index 00000000..49ff6aae --- /dev/null +++ b/src/pulse/rtclock.c @@ -0,0 +1,35 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "rtclock.h" +#include "timeval.h" + +pa_usec_t pa_rtclock_now(void) { + struct timeval tv; + + return pa_timeval_load(pa_rtclock_get(&tv)); +} diff --git a/src/pulse/rtclock.h b/src/pulse/rtclock.h new file mode 100644 index 00000000..eeea6251 --- /dev/null +++ b/src/pulse/rtclock.h @@ -0,0 +1,39 @@ +#ifndef foortclockfoo +#define foortclockfoo + +/*** + This file is part of PulseAudio. + + Copyright 2004-2009 Lennart Poettering + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include + +/** \file + * Monotonic clock utility. */ + +PA_C_DECL_BEGIN + +/** Return the current CLOCK_MONOTONIC time in usec. \since 0.9.16 */ +pa_usec_t pa_rtclock_now(void); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 8df8a5e2..dd44ff40 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -30,13 +30,14 @@ #include #include +#include #include #include #include #include #include -#include +#include #include "fork-detect.h" #include "internal.h" diff --git a/src/pulsecore/core-rtclock.c b/src/pulsecore/core-rtclock.c new file mode 100644 index 00000000..0610e297 --- /dev/null +++ b/src/pulsecore/core-rtclock.c @@ -0,0 +1,152 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_SYS_PRCTL_H +#include +#endif + +#include +#include +#include + +#include "core-rtclock.h" + +pa_usec_t pa_rtclock_age(const struct timeval *tv) { + struct timeval now; + pa_assert(tv); + + return pa_timeval_diff(pa_rtclock_get(&now), tv); +} + +struct timeval *pa_rtclock_get(struct timeval *tv) { +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + +#ifdef CLOCK_MONOTONIC + /* No locking or atomic ops for no_monotonic here */ + static pa_bool_t no_monotonic = FALSE; + + if (!no_monotonic) + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) + no_monotonic = TRUE; + + if (no_monotonic) +#endif + pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); + + pa_assert(tv); + + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + + return tv; + +#else /* HAVE_CLOCK_GETTIME */ + + return pa_gettimeofday(tv); + +#endif +} + +pa_bool_t pa_rtclock_hrtimer(void) { +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + +#ifdef CLOCK_MONOTONIC + if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0) + return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000; +#endif + + pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0); + return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000; + +#else /* HAVE_CLOCK_GETTIME */ + + return FALSE; + +#endif +} + +#define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC)) + +void pa_rtclock_hrtimer_enable(void) { +#ifdef PR_SET_TIMERSLACK + int slack_ns; + + if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) { + pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported."); + return; + } + + pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC)); + + if (slack_ns > TIMER_SLACK_NS) { + slack_ns = TIMER_SLACK_NS; + + pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC)); + + if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) { + pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno)); + return; + } + } + +#endif +} + +struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) { + +#ifdef HAVE_CLOCK_GETTIME + struct timeval wc_now, rt_now; + + pa_gettimeofday(&wc_now); + pa_rtclock_get(&rt_now); + + pa_assert(tv); + + if (pa_timeval_cmp(&wc_now, tv) < 0) + pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now)); + else + pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv)); + + *tv = rt_now; +#endif + + return tv; +} + +pa_usec_t pa_timespec_load(const struct timespec *ts) { + pa_assert(ts); + + return + (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC + + (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC; +} diff --git a/src/pulsecore/core-rtclock.h b/src/pulsecore/core-rtclock.h new file mode 100644 index 00000000..83ff7c7c --- /dev/null +++ b/src/pulsecore/core-rtclock.h @@ -0,0 +1,45 @@ +#ifndef foopulsertclockhfoo +#define foopulsertclockhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +struct timeval; + +/* Something like pulse/timeval.h but based on CLOCK_MONOTONIC */ + +struct timeval *pa_rtclock_get(struct timeval *ts); + +pa_usec_t pa_rtclock_age(const struct timeval *tv); +pa_bool_t pa_rtclock_hrtimer(void); +void pa_rtclock_hrtimer_enable(void); + +/* timer with a resolution better than this are considered high-resolution */ +#define PA_HRTIMER_THRESHOLD_USEC 10 + +struct timeval* pa_rtclock_from_wallclock(struct timeval *tv); + +pa_usec_t pa_timespec_load(const struct timespec *ts); + +#endif diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index acff981b..8c21ee6c 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -38,6 +38,7 @@ #include #endif +#include #include #include #include @@ -45,7 +46,7 @@ #include #include -#include +#include #include #include diff --git a/src/pulsecore/ratelimit.c b/src/pulsecore/ratelimit.c index f00587af..844dd77d 100644 --- a/src/pulsecore/ratelimit.c +++ b/src/pulsecore/ratelimit.c @@ -23,7 +23,8 @@ #include #endif -#include +#include + #include #include diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c deleted file mode 100644 index 53c0258c..00000000 --- a/src/pulsecore/rtclock.c +++ /dev/null @@ -1,158 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - Copyright 2006 Pierre Ossman for Cendio AB - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#ifdef HAVE_SYS_PRCTL_H -#include -#endif - -#include -#include -#include - -#include "rtclock.h" - -pa_usec_t pa_rtclock_age(const struct timeval *tv) { - struct timeval now; - pa_assert(tv); - - return pa_timeval_diff(pa_rtclock_get(&now), tv); -} - -struct timeval *pa_rtclock_get(struct timeval *tv) { -#ifdef HAVE_CLOCK_GETTIME - struct timespec ts; - -#ifdef CLOCK_MONOTONIC - /* No locking or atomic ops for no_monotonic here */ - static pa_bool_t no_monotonic = FALSE; - - if (!no_monotonic) - if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) - no_monotonic = TRUE; - - if (no_monotonic) -#endif - pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); - - pa_assert(tv); - - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / 1000; - - return tv; - -#else /* HAVE_CLOCK_GETTIME */ - - return pa_gettimeofday(tv); - -#endif -} - -pa_bool_t pa_rtclock_hrtimer(void) { -#ifdef HAVE_CLOCK_GETTIME - struct timespec ts; - -#ifdef CLOCK_MONOTONIC - if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0) - return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000; -#endif - - pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0); - return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000; - -#else /* HAVE_CLOCK_GETTIME */ - - return FALSE; - -#endif -} - -#define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC)) - -void pa_rtclock_hrtimer_enable(void) { -#ifdef PR_SET_TIMERSLACK - int slack_ns; - - if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) { - pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported."); - return; - } - - pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC)); - - if (slack_ns > TIMER_SLACK_NS) { - slack_ns = TIMER_SLACK_NS; - - pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC)); - - if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) { - pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno)); - return; - } - } - -#endif -} - -pa_usec_t pa_rtclock_now(void) { - struct timeval tv; - - return pa_timeval_load(pa_rtclock_get(&tv)); -} - -struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) { - -#ifdef HAVE_CLOCK_GETTIME - struct timeval wc_now, rt_now; - - pa_gettimeofday(&wc_now); - pa_rtclock_get(&rt_now); - - pa_assert(tv); - - if (pa_timeval_cmp(&wc_now, tv) < 0) - pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now)); - else - pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv)); - - *tv = rt_now; -#endif - - return tv; -} - -pa_usec_t pa_timespec_load(const struct timespec *ts) { - pa_assert(ts); - - return - (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC + - (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC; -} diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h deleted file mode 100644 index b8a588c0..00000000 --- a/src/pulsecore/rtclock.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef foopulsertclockhfoo -#define foopulsertclockhfoo - -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include - -struct timeval; - -/* Something like pulse/timeval.h but based on CLOCK_MONOTONIC */ - -struct timeval *pa_rtclock_get(struct timeval *ts); - -pa_usec_t pa_rtclock_now(void); - -pa_usec_t pa_rtclock_age(const struct timeval *tv); -pa_bool_t pa_rtclock_hrtimer(void); -void pa_rtclock_hrtimer_enable(void); - -/* timer with a resolution better than this are considered high-resolution */ -#define PA_HRTIMER_THRESHOLD_USEC 10 - -struct timeval* pa_rtclock_from_wallclock(struct timeval *tv); - -pa_usec_t pa_timespec_load(const struct timespec *ts); - -#endif diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index 006b93f6..9401debd 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -44,7 +44,7 @@ #include #include -#include +#include #include #include #include -- cgit From 0955e3d45b6e992308e2d51fcbf28a9f9376f788 Mon Sep 17 00:00:00 2001 From: Marc-AndrĂ© Lureau Date: Sun, 5 Apr 2009 02:13:43 +0300 Subject: Base mainloop on pa_rtclock_now() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the mainloop to monotonic based time events. Introduces 4 helper functions: pa_{context,core}_rttime_{new,restart}(), that fill correctly a timeval with the rtclock flag set if the mainloop supports it. Both mainloop-test and mainloop-test-glib works with rt and timeval based time events. PulseAudio and clients should be fully functional. This patch has received several iterations, and this one as been largely untested. Signed-off-by: Marc-AndrĂ© Lureau --- src/Makefile.am | 6 +++ src/daemon/main.c | 4 +- src/map-file | 2 + src/modules/module-card-restore.c | 12 ++--- src/modules/module-combine.c | 15 ++---- src/modules/module-default-device-restore.c | 13 ++---- src/modules/module-device-restore.c | 12 ++--- src/modules/module-esound-sink.c | 2 +- src/modules/module-stream-restore.c | 11 ++--- src/modules/module-suspend-on-idle.c | 19 ++++---- src/modules/module-tunnel.c | 18 +++----- src/modules/module-waveout.c | 4 +- src/modules/raop/raop_client.c | 2 +- src/modules/rtp/module-rtp-recv.c | 15 ++---- src/modules/rtp/module-rtp-send.c | 14 ++---- src/modules/rtp/rtsp_client.c | 2 +- src/pulse/context.c | 29 ++++++++++-- src/pulse/context.h | 8 ++++ src/pulse/internal.h | 1 + src/pulse/mainloop-api.h | 1 + src/pulse/mainloop.c | 71 +++++++++++++++++++---------- src/pulse/stream.c | 13 ++---- src/pulse/timeval.h | 3 ++ src/pulsecore/avahi-wrap.c | 9 ++-- src/pulsecore/core-rtclock.c | 35 ++++++++++++++ src/pulsecore/core-rtclock.h | 5 ++ src/pulsecore/core-scache.c | 19 +++----- src/pulsecore/core.c | 28 +++++++++--- src/pulsecore/core.h | 4 ++ src/pulsecore/dbus-shared.c | 2 +- src/pulsecore/dbus-util.c | 58 ++++++++++++++--------- src/pulsecore/dbus-util.h | 2 +- src/pulsecore/pdispatch.c | 13 +++--- src/pulsecore/pdispatch.h | 2 +- src/pulsecore/protocol-esound.c | 15 +++--- src/pulsecore/protocol-native.c | 17 +++---- src/pulsecore/socket-client.c | 22 ++++----- src/pulsecore/socket-client.h | 2 +- src/tests/mainloop-test.c | 5 +- src/tests/thread-mainloop-test.c | 7 ++- src/utils/pabrowse.c | 3 +- src/utils/pacat.c | 19 ++------ 42 files changed, 311 insertions(+), 233 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 53c21451..b4904803 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -64,6 +64,11 @@ AM_CFLAGS = \ -I$(top_srcdir)/src/modules/alsa \ -I$(top_builddir)/src/modules/alsa \ -I$(top_srcdir)/src/modules/raop \ + -I$(top_builddir)/src/modules/raop \ + -I$(top_srcdir)/src/modules/x11 \ + -I$(top_builddir)/src/modules/x11 \ + -I$(top_srcdir)/src/modules/jack \ + -I$(top_builddir)/src/modules/jack \ $(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS \ $(LIBSAMPLERATE_CFLAGS) \ $(LIBSNDFILE_CFLAGS) \ @@ -688,6 +693,7 @@ pulseinclude_HEADERS = \ pulse/operation.h \ pulse/proplist.h \ pulse/pulseaudio.h \ + pulse/rtclock.h \ pulse/sample.h \ pulse/scache.h \ pulse/simple.h \ diff --git a/src/daemon/main.c b/src/daemon/main.c index 083e4eae..399034e9 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -133,7 +133,7 @@ static void message_cb(pa_mainloop_api*a, pa_time_event*e, const struct timeval } pa_timeval_add(pa_gettimeofday(&tvnext), 100000); - a->time_restart(e, &tvnext); + a->rtclock_time_restart(e, &tvnext); } #endif @@ -1018,7 +1018,7 @@ int main(int argc, char *argv[]) { #endif #ifdef OS_IS_WIN32 - win32_timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL); + win32_timer = pa_mainloop_get_api(mainloop)->rtclock_time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL); #endif oil_init(); diff --git a/src/map-file b/src/map-file index e96eac51..a1d0a061 100644 --- a/src/map-file +++ b/src/map-file @@ -86,6 +86,8 @@ pa_context_ref; pa_context_remove_autoload_by_index; pa_context_remove_autoload_by_name; pa_context_remove_sample; +pa_context_rttime_new; +pa_context_rttime_restart; pa_context_set_card_profile_by_index; pa_context_set_card_profile_by_name; pa_context_set_default_sink; diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c index 85583b2c..6b057b4c 100644 --- a/src/modules/module-card-restore.c +++ b/src/modules/module-card-restore.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -53,7 +54,7 @@ PA_MODULE_DESCRIPTION("Automatically restore profile of cards"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); -#define SAVE_INTERVAL 10 +#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) static const char* const valid_modargs[] = { NULL @@ -75,12 +76,11 @@ struct entry { char profile[PA_NAME_MAX]; } PA_GCC_PACKED ; -static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { +static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { struct userdata *u = userdata; pa_assert(a); pa_assert(e); - pa_assert(tv); pa_assert(u); pa_assert(e == u->save_time_event); @@ -132,14 +132,10 @@ fail: } static void trigger_save(struct userdata *u) { - struct timeval tv; - if (u->save_time_event) return; - pa_gettimeofday(&tv); - tv.tv_sec += SAVE_INTERVAL; - u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u); + u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u); } static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 6ca7cd99..757dbda4 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -225,9 +225,8 @@ static void adjust_rates(struct userdata *u) { pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, NULL, (int64_t) avg_total_latency, NULL); } -static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { +static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) { struct userdata *u = userdata; - struct timeval n; pa_assert(u); pa_assert(a); @@ -235,9 +234,7 @@ static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct time adjust_rates(u); - pa_gettimeofday(&n); - n.tv_sec += (time_t) u->adjust_time; - u->sink->core->mainloop->time_restart(e, &n); + pa_core_rttime_restart(u->core, e, pa_rtclock_now() + u->adjust_time * PA_USEC_PER_SEC); } static void process_render_null(struct userdata *u, pa_usec_t now) { @@ -1171,12 +1168,8 @@ int pa__init(pa_module*m) { if (o->sink_input) pa_sink_input_put(o->sink_input); - if (u->adjust_time > 0) { - struct timeval tv; - pa_gettimeofday(&tv); - tv.tv_sec += (time_t) u->adjust_time; - u->time_event = m->core->mainloop->time_new(m->core->mainloop, &tv, time_callback, u); - } + if (u->adjust_time > 0) + u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time * PA_USEC_PER_SEC, time_callback, u); pa_modargs_free(ma); diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c index 0092d1c7..27ae60e5 100644 --- a/src/modules/module-default-device-restore.c +++ b/src/modules/module-default-device-restore.c @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -42,7 +43,7 @@ PA_MODULE_DESCRIPTION("Automatically restore the default sink and source"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); -#define DEFAULT_SAVE_INTERVAL 5 +#define SAVE_INTERVAL (5 * PA_USEC_PER_SEC) struct userdata { pa_core *core; @@ -127,7 +128,7 @@ static void save(struct userdata *u) { u->modified = FALSE; } -static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) { +static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) { struct userdata *u = userdata; pa_assert(u); @@ -146,12 +147,8 @@ static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t id u->modified = TRUE; - if (!u->time_event) { - struct timeval tv; - pa_gettimeofday(&tv); - pa_timeval_add(&tv, DEFAULT_SAVE_INTERVAL*PA_USEC_PER_SEC); - u->time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, time_cb, u); - } + if (!u->time_event) + u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, time_cb, u); } int pa__init(pa_module *m) { diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index a2745b88..8c15d454 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -58,7 +59,7 @@ PA_MODULE_USAGE( "restore_volume= " "restore_muted="); -#define SAVE_INTERVAL 10 +#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) static const char* const valid_modargs[] = { "restore_volume", @@ -95,12 +96,11 @@ struct entry { char port[PA_NAME_MAX]; } PA_GCC_PACKED; -static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { +static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { struct userdata *u = userdata; pa_assert(a); pa_assert(e); - pa_assert(tv); pa_assert(u); pa_assert(e == u->save_time_event); @@ -162,14 +162,10 @@ fail: } static void trigger_save(struct userdata *u) { - struct timeval tv; - if (u->save_time_event) return; - pa_gettimeofday(&tv); - tv.tv_sec += SAVE_INTERVAL; - u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u); + u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u); } static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 2df8a0e8..b26b465d 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -609,7 +609,7 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - if (!(u->client = pa_socket_client_new_string(u->core->mainloop, espeaker, ESD_DEFAULT_PORT))) { + if (!(u->client = pa_socket_client_new_string(u->core->mainloop, TRUE, espeaker, ESD_DEFAULT_PORT))) { pa_log("Failed to connect to server."); goto fail; } diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index f2aea27b..99c69f66 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -61,7 +62,7 @@ PA_MODULE_USAGE( "restore_volume= " "restore_muted="); -#define SAVE_INTERVAL 10 +#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) #define IDENTIFICATION_PROPERTY "module-stream-restore.id" static const char* const valid_modargs[] = { @@ -111,12 +112,11 @@ enum { SUBCOMMAND_EVENT }; -static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { +static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { struct userdata *u = userdata; pa_assert(a); pa_assert(e); - pa_assert(tv); pa_assert(u); pa_assert(e == u->save_time_event); @@ -210,7 +210,6 @@ fail: } static void trigger_save(struct userdata *u) { - struct timeval tv; pa_native_connection *c; uint32_t idx; @@ -230,9 +229,7 @@ static void trigger_save(struct userdata *u) { if (u->save_time_event) return; - pa_gettimeofday(&tv); - tv.tv_sec += SAVE_INTERVAL; - u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u); + u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u); } static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index c5b78911..70a7b049 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -75,11 +76,11 @@ struct device_info { struct userdata *userdata; pa_sink *sink; pa_source *source; - struct timeval last_use; + pa_usec_t last_use; pa_time_event *time_event; }; -static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { +static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { struct device_info *d = userdata; pa_assert(d); @@ -98,22 +99,20 @@ static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval } static void restart(struct device_info *d) { - struct timeval tv; + pa_usec_t now; const char *s; uint32_t timeout; + pa_assert(d); pa_assert(d->sink || d->source); - pa_gettimeofday(&tv); - d->last_use = tv; + d->last_use = now = pa_rtclock_now(); s = pa_proplist_gets(d->sink ? d->sink->proplist : d->source->proplist, "module-suspend-on-idle.timeout"); if (!s || pa_atou(s, &timeout) < 0) - timeout = d->userdata->timeout; - - pa_timeval_add(&tv, timeout * PA_USEC_PER_SEC); + timeout = d->userdata->timeout; - d->userdata->core->mainloop->time_restart(d->time_event, &tv); + pa_core_rttime_restart(d->userdata->core, d->time_event, now + timeout * PA_USEC_PER_SEC); if (d->sink) pa_log_debug("Sink %s becomes idle, timeout in %u seconds.", d->sink->name, timeout); @@ -338,7 +337,7 @@ static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct user d->userdata = u; d->source = source ? pa_source_ref(source) : NULL; d->sink = sink ? pa_sink_ref(sink) : NULL; - d->time_event = c->mainloop->time_new(c->mainloop, NULL, timeout_cb, d); + d->time_event = pa_core_rttime_new(c, PA_USEC_INVALID, timeout_cb, d); pa_hashmap_put(u->device_infos, o, d); if ((d->sink && pa_sink_check_suspend(d->sink) <= 0) || diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index ec4808f0..ce37bf47 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -113,7 +113,7 @@ static const char* const valid_modargs[] = { #define DEFAULT_TIMEOUT 5 -#define LATENCY_INTERVAL 10 +#define LATENCY_INTERVAL (10*PA_USEC_PER_SEC) #define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC) @@ -879,9 +879,8 @@ static void request_latency(struct userdata *u) { } /* Called from main context */ -static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) { +static void timeout_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { struct userdata *u = userdata; - struct timeval ntv; pa_assert(m); pa_assert(e); @@ -889,9 +888,7 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct request_latency(u); - pa_gettimeofday(&ntv); - ntv.tv_sec += LATENCY_INTERVAL; - m->time_restart(e, &ntv); + pa_core_rttime_restart(u->core, e, pa_rtclock_now() + LATENCY_INTERVAL); } /* Called from main context */ @@ -1358,7 +1355,6 @@ static void start_subscribe(struct userdata *u) { /* Called from main context */ static void create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; - struct timeval ntv; #ifdef TUNNEL_SINK uint32_t bytes; #endif @@ -1440,9 +1436,7 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t request_info(u); pa_assert(!u->time_event); - pa_gettimeofday(&ntv); - ntv.tv_sec += LATENCY_INTERVAL; - u->time_event = u->core->mainloop->time_new(u->core->mainloop, &ntv, timeout_callback, u); + u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + LATENCY_INTERVAL, timeout_callback, u); request_latency(u); @@ -1707,7 +1701,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata } u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->mempool); - u->pdispatch = pa_pdispatch_new(u->core->mainloop, command_table, PA_COMMAND_MAX); + u->pdispatch = pa_pdispatch_new(u->core->mainloop, TRUE, command_table, PA_COMMAND_MAX); pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u); pa_pstream_set_recieve_packet_callback(u->pstream, pstream_packet_callback, u); @@ -1854,7 +1848,7 @@ int pa__init(pa_module*m) { goto fail; } - if (!(u->client = pa_socket_client_new_string(m->core->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) { + if (!(u->client = pa_socket_client_new_string(m->core->mainloop, TRUE, u->server_name, PA_NATIVE_DEFAULT_PORT))) { pa_log("Failed to connect to server '%s'", u->server_name); goto fail; } diff --git a/src/modules/module-waveout.c b/src/modules/module-waveout.c index 2d35828d..d1b9f2ff 100644 --- a/src/modules/module-waveout.c +++ b/src/modules/module-waveout.c @@ -256,7 +256,7 @@ static void poll_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *t pa_gettimeofday(&ntv); pa_timeval_add(&ntv, u->poll_timeout); - a->time_restart(e, &ntv); + a->rtclock_time_restart(e, &ntv); } static void defer_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { @@ -549,7 +549,7 @@ int pa__init(pa_core *c, pa_module*m) { pa_gettimeofday(&tv); pa_timeval_add(&tv, u->poll_timeout); - u->event = c->mainloop->time_new(c->mainloop, &tv, poll_cb, u); + u->event = c->mainloop->rtclock_time_new(c->mainloop, &tv, poll_cb, u); assert(u->event); u->defer = c->mainloop->defer_new(c->mainloop, defer_cb, u); diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c index b3f243c3..c4b02371 100644 --- a/src/modules/raop/raop_client.c +++ b/src/modules/raop/raop_client.c @@ -331,7 +331,7 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he uint32_t port = pa_rtsp_serverport(c->rtsp); pa_log_debug("RAOP: RECORDED"); - if (!(c->sc = pa_socket_client_new_string(c->core->mainloop, c->host, port))) { + if (!(c->sc = pa_socket_client_new_string(c->core->mainloop, TRUE, c->host, port))) { pa_log("failed to connect to server '%s:%d'", c->host, port); return; } diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index 84cf07de..5caf8272 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -113,6 +113,7 @@ struct session { struct userdata { pa_module *module; + pa_core *core; pa_sap_context sap_context; pa_io_event* sap_event; @@ -622,15 +623,13 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event } } -static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *ptv, void *userdata) { +static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) { struct session *s, *n; struct userdata *u = userdata; struct timeval now; - struct timeval tv; pa_assert(m); pa_assert(t); - pa_assert(ptv); pa_assert(u); pa_rtclock_get(&now); @@ -648,9 +647,7 @@ static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const str } /* Restart timer */ - pa_gettimeofday(&tv); - pa_timeval_add(&tv, DEATH_TIMEOUT*PA_USEC_PER_SEC); - m->time_restart(t, &tv); + pa_core_rttime_restart(u->module->core, t, pa_rtclock_now() + DEATH_TIMEOUT * PA_USEC_PER_SEC); } int pa__init(pa_module*m) { @@ -664,7 +661,6 @@ int pa__init(pa_module*m) { socklen_t salen; const char *sap_address; int fd = -1; - struct timeval tv; pa_assert(m); @@ -697,6 +693,7 @@ int pa__init(pa_module*m) { m->userdata = u = pa_xnew(struct userdata, 1); u->module = m; + u->core = m->core; u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); u->sap_event = m->core->mainloop->io_new(m->core->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u); @@ -706,9 +703,7 @@ int pa__init(pa_module*m) { u->n_sessions = 0; u->by_origin = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - pa_gettimeofday(&tv); - pa_timeval_add(&tv, DEATH_TIMEOUT * PA_USEC_PER_SEC); - u->check_death_event = m->core->mainloop->time_new(m->core->mainloop, &tv, check_death_event_cb, u); + u->check_death_event = pa_core_rttime_new(m->core, pa_rtclock_now() + DEATH_TIMEOUT * PA_USEC_PER_SEC, check_death_event_cb, u); pa_modargs_free(ma); diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index 39ee4d75..f147364d 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -77,7 +78,7 @@ PA_MODULE_USAGE( #define DEFAULT_DESTINATION "224.0.0.56" #define MEMBLOCKQ_MAXLENGTH (1024*170) #define DEFAULT_MTU 1280 -#define SAP_INTERVAL 5 +#define SAP_INTERVAL (5*PA_USEC_PER_SEC) static const char* const valid_modargs[] = { "source", @@ -151,18 +152,14 @@ static void source_output_kill(pa_source_output* o) { static void sap_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) { struct userdata *u = userdata; - struct timeval next; pa_assert(m); pa_assert(t); - pa_assert(tv); pa_assert(u); pa_sap_send(&u->sap_context, 0); - pa_gettimeofday(&next); - pa_timeval_add(&next, SAP_INTERVAL * PA_USEC_PER_SEC); - m->time_restart(t, &next); + pa_core_rttime_restart(u->module->core, t, pa_rtclock_now() + SAP_INTERVAL); } int pa__init(pa_module*m) { @@ -186,7 +183,6 @@ int pa__init(pa_module*m) { char *p; int r, j; socklen_t k; - struct timeval tv; char hn[128], *n; pa_bool_t loop = FALSE; pa_source_output_new_data data; @@ -395,9 +391,7 @@ int pa__init(pa_module*m) { pa_sap_send(&u->sap_context, 0); - pa_gettimeofday(&tv); - pa_timeval_add(&tv, SAP_INTERVAL * PA_USEC_PER_SEC); - u->sap_event = m->core->mainloop->time_new(m->core->mainloop, &tv, sap_event_cb, u); + u->sap_event = pa_core_rttime_new(m->core, pa_rtclock_now() + SAP_INTERVAL, sap_event_cb, u); pa_source_output_put(u->source_output); diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c index cb037de6..72d304e8 100644 --- a/src/modules/rtp/rtsp_client.c +++ b/src/modules/rtp/rtsp_client.c @@ -333,7 +333,7 @@ int pa_rtsp_connect(pa_rtsp_client *c) { pa_xfree(c->session); c->session = NULL; - if (!(c->sc = pa_socket_client_new_string(c->mainloop, c->hostname, c->port))) { + if (!(c->sc = pa_socket_client_new_string(c->mainloop, TRUE, c->hostname, c->port))) { pa_log("failed to connect to server '%s:%d'", c->hostname, c->port); return -1; } diff --git a/src/pulse/context.c b/src/pulse/context.c index 3b7bf08d..0c1810f3 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -540,7 +541,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) { pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); pa_assert(!c->pdispatch); - c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); + c->pdispatch = pa_pdispatch_new(c->mainloop, c->use_rtclock, command_table, PA_COMMAND_MAX); if (!c->conf->cookie_valid) pa_log_info(_("No cookie loaded. Attempting to connect without.")); @@ -757,7 +758,7 @@ static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wr pa_assert(conn); dbus_error_init(&error); - if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, type, &error)) || dbus_error_is_set(&error)) { + if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, c->use_rtclock, type, &error)) || dbus_error_is_set(&error)) { pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message); goto finish; } @@ -827,7 +828,7 @@ static int try_next_connection(pa_context *c) { pa_xfree(c->server); c->server = pa_xstrdup(u); - if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) + if (!(c->client = pa_socket_client_new_string(c->mainloop, c->use_rtclock, u, PA_NATIVE_DEFAULT_PORT))) continue; c->is_local = !!pa_socket_client_is_local(c->client); @@ -1443,3 +1444,25 @@ finish: if (pl) pa_proplist_free(pl); } + +pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) { + struct timeval tv; + + pa_assert(c); + pa_assert(c->mainloop); + + pa_timeval_rtstore(&tv, usec, c->use_rtclock); + + return c->mainloop->time_new(c->mainloop, &tv, cb, userdata); +} + +void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) { + struct timeval tv; + + pa_assert(c); + pa_assert(c->mainloop); + + pa_timeval_rtstore(&tv, usec, c->use_rtclock); + + c->mainloop->time_restart(e, &tv); +} diff --git a/src/pulse/context.h b/src/pulse/context.h index 139d0e0b..cd129313 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -260,6 +260,14 @@ pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[] * introspection functions, such as pa_context_get_client_info(). \since 0.9.11 */ uint32_t pa_context_get_index(pa_context *s); +/** Create a new timer event source for the specified time (wrapper + for mainloop->time_new). \since 0.9.16 */ +pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata); +/** Restart a running or expired timer event source (wrapper + for mainloop->time_restart). \since 0.9.16 */ +void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec); + + PA_C_DECL_END #endif diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 28a989b3..fa5efd7d 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -89,6 +89,7 @@ struct pa_context { pa_bool_t server_specified:1; pa_bool_t no_fail:1; pa_bool_t do_autospawn:1; + pa_bool_t use_rtclock:1; pa_spawn_api spawn_api; pa_strlist *server_list; diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h index e353ed96..aa0d5e73 100644 --- a/src/pulse/mainloop-api.h +++ b/src/pulse/mainloop-api.h @@ -27,6 +27,7 @@ #include #include +#include #include /** \file diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 225fd098..1b779468 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -42,10 +42,12 @@ #include #endif +#include +#include #include #include -#include +#include #include #include #include @@ -75,7 +77,7 @@ struct pa_time_event { pa_bool_t dead:1; pa_bool_t enabled:1; - struct timeval timeval; + pa_usec_t time; pa_time_event_cb_t callback; void *userdata; @@ -317,6 +319,21 @@ static void mainloop_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy } /* Time events */ +static pa_usec_t timeval_load(struct timeval *tv) { + pa_bool_t is_rtclock; + + if (!tv) + return PA_USEC_INVALID; + + is_rtclock = !!(tv->tv_usec & PA_TIMEVAL_RTCLOCK); + tv->tv_usec &= ~PA_TIMEVAL_RTCLOCK; + + if (!is_rtclock) + pa_rtclock_from_wallclock(tv); + + return pa_timeval_load(tv); +} + static pa_time_event* mainloop_time_new( pa_mainloop_api*a, const struct timeval *tv, @@ -325,11 +342,14 @@ static pa_time_event* mainloop_time_new( pa_mainloop *m; pa_time_event *e; + pa_usec_t t; + struct timeval ttv; pa_assert(a); pa_assert(a->userdata); pa_assert(callback); + t = timeval_load(tv? ttv = *tv, &ttv : NULL); m = a->userdata; pa_assert(a == &m->api); @@ -337,15 +357,15 @@ static pa_time_event* mainloop_time_new( e->mainloop = m; e->dead = FALSE; - if ((e->enabled = !!tv)) { - e->timeval = *tv; + if ((e->enabled = (t != PA_USEC_INVALID))) { + e->time = t; m->n_enabled_time_events++; if (m->cached_next_time_event) { pa_assert(m->cached_next_time_event->enabled); - if (pa_timeval_cmp(tv, &m->cached_next_time_event->timeval) < 0) + if (t < m->cached_next_time_event->time) m->cached_next_time_event = e; } } @@ -363,24 +383,30 @@ static pa_time_event* mainloop_time_new( } static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { + pa_bool_t valid; + pa_usec_t t; + struct timeval ttv; + pa_assert(e); pa_assert(!e->dead); - if (e->enabled && !tv) { + t = timeval_load(tv? ttv = *tv, &ttv : NULL); + valid = (t != PA_USEC_INVALID); + if (e->enabled && !valid) { pa_assert(e->mainloop->n_enabled_time_events > 0); e->mainloop->n_enabled_time_events--; - } else if (!e->enabled && tv) + } else if (!e->enabled && valid) e->mainloop->n_enabled_time_events++; - if ((e->enabled = !!tv)) { - e->timeval = *tv; + if ((e->enabled = valid)) { + e->time = t; pa_mainloop_wakeup(e->mainloop); } if (e->mainloop->cached_next_time_event && e->enabled) { pa_assert(e->mainloop->cached_next_time_event->enabled); - if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0) + if (t < e->mainloop->cached_next_time_event->time) e->mainloop->cached_next_time_event = e; } else if (e->mainloop->cached_next_time_event == e) e->mainloop->cached_next_time_event = NULL; @@ -721,11 +747,11 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) { if (t->dead || !t->enabled) continue; - if (!n || pa_timeval_cmp(&t->timeval, &n->timeval) < 0) { + if (!n || t->time < n->time) { n = t; - /* Shortcut for tv = { 0, 0 } */ - if (n->timeval.tv_sec <= 0) + /* Shortcut for time == 0 */ + if (n->time == 0) break; } } @@ -736,7 +762,6 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) { static int calc_next_timeout(pa_mainloop *m) { pa_time_event *t; - struct timeval now; pa_usec_t usec; if (!m->n_enabled_time_events) @@ -745,41 +770,41 @@ static int calc_next_timeout(pa_mainloop *m) { t = find_next_time_event(m); pa_assert(t); - if (t->timeval.tv_sec <= 0) + if (t->time == 0) return 0; - pa_gettimeofday(&now); + usec = t->time - pa_rtclock_now(); - if (pa_timeval_cmp(&t->timeval, &now) <= 0) + if (usec <= 0) return 0; - usec = pa_timeval_diff(&t->timeval, &now); - return (int) (usec / 1000); + return (int) (usec / 1000); /* in milliseconds */ } static int dispatch_timeout(pa_mainloop *m) { pa_time_event *e; - struct timeval now; + pa_usec_t now; int r = 0; pa_assert(m); if (m->n_enabled_time_events <= 0) return 0; - pa_gettimeofday(&now); + now = pa_rtclock_now(); for (e = m->time_events; e && !m->quit; e = e->next) { if (e->dead || !e->enabled) continue; - if (pa_timeval_cmp(&e->timeval, &now) <= 0) { + if (e->time <= now) { + struct timeval tv; pa_assert(e->callback); /* Disable time event */ mainloop_time_restart(e, NULL); - e->callback(&m->api, e, &e->timeval, e->userdata); + e->callback(&m->api, e, pa_timeval_rtstore(&tv, e->time, TRUE), e->userdata); r++; } diff --git a/src/pulse/stream.c b/src/pulse/stream.c index dd44ff40..40556329 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -320,14 +320,10 @@ static void request_auto_timing_update(pa_stream *s, pa_bool_t force) { } if (s->auto_timing_update_event) { - struct timeval next; - if (force) s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC; - pa_gettimeofday(&next); - pa_timeval_add(&next, s->auto_timing_interval_usec); - s->mainloop->time_restart(s->auto_timing_update_event, &next); + pa_context_rttime_restart(s->context, s->auto_timing_update_event, pa_rtclock_now() + s->auto_timing_interval_usec); s->auto_timing_interval_usec = PA_MIN(AUTO_TIMING_INTERVAL_END_USEC, s->auto_timing_interval_usec*2); } @@ -801,7 +797,7 @@ static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) { request_auto_timing_update(s, TRUE); } -static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) { +static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { pa_stream *s = userdata; pa_assert(s); @@ -823,12 +819,9 @@ static void create_stream_complete(pa_stream *s) { s->write_callback(s, (size_t) s->requested_bytes, s->write_userdata); if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { - struct timeval tv; - pa_gettimeofday(&tv); s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC; - pa_timeval_add(&tv, s->auto_timing_interval_usec); pa_assert(!s->auto_timing_update_event); - s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s); + s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s); request_auto_timing_update(s, TRUE); } diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index 651da953..6307735c 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -51,6 +51,9 @@ PA_C_DECL_BEGIN /** The number of nanoseconds in a microsecond */ #define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL) +/** Invalid time in usec */ +#define PA_USEC_INVALID ((pa_usec_t) -1) + struct timeval; /** Return the current timestamp, just like UNIX gettimeofday() */ diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c index 56d9d3dd..f1f08bcc 100644 --- a/src/pulsecore/avahi-wrap.c +++ b/src/pulsecore/avahi-wrap.c @@ -23,6 +23,7 @@ #include #endif +#include #include #include @@ -116,14 +117,13 @@ struct AvahiTimeout { void *userdata; }; -static void timeout_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { - AvahiTimeout *t = userdata; +static void timeout_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { + AvahiTimeout *to = userdata; pa_assert(a); pa_assert(e); - pa_assert(t); - t->callback(t, t->userdata); + to->callback(to, to->userdata); } static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) { @@ -145,6 +145,7 @@ static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, } static void timeout_update(AvahiTimeout *t, const struct timeval *tv) { + pa_assert(t); if (t->time_event && tv) diff --git a/src/pulsecore/core-rtclock.c b/src/pulsecore/core-rtclock.c index 0610e297..1fa71c80 100644 --- a/src/pulsecore/core-rtclock.c +++ b/src/pulsecore/core-rtclock.c @@ -150,3 +150,38 @@ pa_usec_t pa_timespec_load(const struct timespec *ts) { (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC + (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC; } + + +static struct timeval* wallclock_from_rtclock(struct timeval *tv) { + +#ifdef HAVE_CLOCK_GETTIME + struct timeval wc_now, rt_now; + + pa_gettimeofday(&wc_now); + pa_rtclock_get(&rt_now); + + pa_assert(tv); + + if (pa_timeval_cmp(&rt_now, tv) < 0) + pa_timeval_add(&wc_now, pa_timeval_diff(tv, &rt_now)); + else + pa_timeval_sub(&wc_now, pa_timeval_diff(&rt_now, tv)); + + *tv = wc_now; +#endif + + return tv; +} + +struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, pa_bool_t rtclock) { + pa_assert(tv); + + pa_timeval_store(tv, v); + + if (rtclock) + tv->tv_usec |= PA_TIMEVAL_RTCLOCK; + else + wallclock_from_rtclock(tv); + + return tv; +} diff --git a/src/pulsecore/core-rtclock.h b/src/pulsecore/core-rtclock.h index 83ff7c7c..3c90068b 100644 --- a/src/pulsecore/core-rtclock.h +++ b/src/pulsecore/core-rtclock.h @@ -38,8 +38,13 @@ void pa_rtclock_hrtimer_enable(void); /* timer with a resolution better than this are considered high-resolution */ #define PA_HRTIMER_THRESHOLD_USEC 10 +/* bit to set in tv.tv_usec to mark that the timeval is in monotonic time */ +#define PA_TIMEVAL_RTCLOCK (1 << 30) + struct timeval* pa_rtclock_from_wallclock(struct timeval *tv); pa_usec_t pa_timespec_load(const struct timespec *ts); +struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, pa_bool_t rtclock); + #endif diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c index 086f5fcb..4c5a4b26 100644 --- a/src/pulsecore/core-scache.c +++ b/src/pulsecore/core-scache.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -61,11 +63,10 @@ #include "core-scache.h" -#define UNLOAD_POLL_TIME 60 +#define UNLOAD_POLL_TIME (60 * PA_USEC_PER_SEC) -static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) { +static void timeout_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { pa_core *c = userdata; - struct timeval ntv; pa_assert(c); pa_assert(c->mainloop == m); @@ -73,9 +74,7 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct t pa_scache_unload_unused(c); - pa_gettimeofday(&ntv); - ntv.tv_sec += UNLOAD_POLL_TIME; - m->time_restart(e, &ntv); + pa_core_rttime_restart(c, e, pa_rtclock_now() + UNLOAD_POLL_TIME); } static void free_entry(pa_scache_entry *e) { @@ -256,12 +255,8 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename); - if (!c->scache_auto_unload_event) { - struct timeval ntv; - pa_gettimeofday(&ntv); - ntv.tv_sec += UNLOAD_POLL_TIME; - c->scache_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c); - } + if (!c->scache_auto_unload_event) + c->scache_auto_unload_event = pa_core_rttime_new(c, pa_rtclock_now() + UNLOAD_POLL_TIME, timeout_callback, c); if (idx) *idx = e->index; diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 06573f17..f5eb8352 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -214,7 +216,7 @@ static void core_free(pa_object *o) { pa_xfree(c); } -static void exit_callback(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { +static void exit_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { pa_core *c = userdata; pa_assert(c->exit_event == e); @@ -229,11 +231,7 @@ void pa_core_check_idle(pa_core *c) { c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) { - struct timeval tv; - pa_gettimeofday(&tv); - tv.tv_sec+= c->exit_idle_time; - - c->exit_event = c->mainloop->time_new(c->mainloop, &tv, exit_callback, c); + c->exit_event = pa_core_rttime_new(c, pa_rtclock_now() + c->exit_idle_time * PA_USEC_PER_SEC, exit_callback, c); } else if (c->exit_event && pa_idxset_size(c->clients) > 0) { c->mainloop->time_free(c->exit_event); @@ -261,3 +259,21 @@ void pa_core_maybe_vacuum(pa_core *c) { pa_log_debug("Hmm, no streams around, trying to vacuum."); pa_mempool_vacuum(c->mempool); } + +pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) { + struct timeval tv; + + pa_assert(c); + pa_assert(c->mainloop); + + return c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, usec, TRUE), cb, userdata); +} + +void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec) { + struct timeval tv; + + pa_assert(c); + pa_assert(c->mainloop); + + c->mainloop->time_restart(e, pa_timeval_rtstore(&tv, usec, TRUE)); +} diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 09a880c4..e7abd61b 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -183,4 +183,8 @@ int pa_core_exit(pa_core *c, pa_bool_t force, int retval); void pa_core_maybe_vacuum(pa_core *c); +/* wrapper for c->mainloop->time_*() RT time events */ +pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata); +void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec); + #endif diff --git a/src/pulsecore/dbus-shared.c b/src/pulsecore/dbus-shared.c index 9d9445b6..20ef9b1e 100644 --- a/src/pulsecore/dbus-shared.c +++ b/src/pulsecore/dbus-shared.c @@ -70,7 +70,7 @@ pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *err if ((pconn = pa_shared_get(c, prop_name[type]))) return pa_dbus_connection_ref(pconn); - if (!(conn = pa_dbus_wrap_connection_new(c->mainloop, type, error))) + if (!(conn = pa_dbus_wrap_connection_new(c->mainloop, TRUE, type, error))) return NULL; return dbus_connection_new(c, conn, prop_name[type]); diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index ece36def..4e6148f0 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -26,9 +26,11 @@ #include -#include +#include #include +#include +#include #include #include @@ -38,6 +40,12 @@ struct pa_dbus_wrap_connection { pa_mainloop_api *mainloop; DBusConnection *connection; pa_defer_event* dispatch_event; + pa_bool_t use_rtclock:1; +}; + +struct timeout_data { + pa_dbus_wrap_connection *c; + DBusTimeout *timeout; }; static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) { @@ -118,16 +126,18 @@ static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_e } /* pa_time_event_cb_t timer event handler */ -static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) { - DBusTimeout *timeout = userdata; +static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *t, void *userdata) { + struct timeval tv; + struct timeout_data *d = userdata; - if (dbus_timeout_get_enabled(timeout)) { - struct timeval next = *tv; - dbus_timeout_handle(timeout); + pa_assert(d); + pa_assert(d->c); + + if (dbus_timeout_get_enabled(d->timeout)) { + dbus_timeout_handle(d->timeout); /* restart it for the next scheduled time */ - pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); - ea->time_restart(e, &next); + ea->time_restart(e, pa_timeval_rtstore(&tv, pa_timeval_load(t) + dbus_timeout_get_interval(d->timeout) * PA_USEC_PER_MSEC, d->c->use_rtclock)); } } @@ -179,11 +189,16 @@ static void toggle_watch(DBusWatch *watch, void *data) { c->mainloop->io_enable(ev, get_watch_flags(watch)); } +static void time_event_destroy_cb(pa_mainloop_api *a, pa_time_event *e, void *userdata) { + pa_xfree(userdata); +} + /* DBusAddTimeoutFunction callback for pa mainloop */ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { pa_dbus_wrap_connection *c = data; pa_time_event *ev; struct timeval tv; + struct timeout_data *d; pa_assert(timeout); pa_assert(c); @@ -191,10 +206,11 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { if (!dbus_timeout_get_enabled(timeout)) return FALSE; - pa_gettimeofday(&tv); - pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); - - ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout); + d = pa_xnew(struct timeout_data, 1); + d->c = c; + d->timeout = timeout; + ev = c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + dbus_timeout_get_interval(timeout) * PA_USEC_PER_MSEC, c->use_rtclock), handle_time_event, d); + c->mainloop->time_set_destroy(ev, time_event_destroy_cb); dbus_timeout_set_data(timeout, ev, NULL); @@ -215,23 +231,20 @@ static void remove_timeout(DBusTimeout *timeout, void *data) { /* DBusTimeoutToggledFunction callback for pa mainloop */ static void toggle_timeout(DBusTimeout *timeout, void *data) { - pa_dbus_wrap_connection *c = data; + struct timeout_data *d = data; pa_time_event *ev; + struct timeval tv; + pa_assert(d); + pa_assert(d->c); pa_assert(timeout); - pa_assert(c); pa_assert_se(ev = dbus_timeout_get_data(timeout)); if (dbus_timeout_get_enabled(timeout)) { - struct timeval tv; - - pa_gettimeofday(&tv); - pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); - - c->mainloop->time_restart(ev, &tv); + d->c->mainloop->time_restart(ev, pa_timeval_rtstore(&tv, pa_rtclock_now() + dbus_timeout_get_interval(timeout) * PA_USEC_PER_MSEC, d->c->use_rtclock)); } else - c->mainloop->time_restart(ev, NULL); + d->c->mainloop->time_restart(ev, pa_timeval_rtstore(&tv, PA_USEC_INVALID, d->c->use_rtclock)); } static void wakeup_main(void *userdata) { @@ -244,7 +257,7 @@ static void wakeup_main(void *userdata) { c->mainloop->defer_enable(c->dispatch_event, 1); } -pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBusType type, DBusError *error) { +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, pa_bool_t use_rtclock, DBusBusType type, DBusError *error) { DBusConnection *conn; pa_dbus_wrap_connection *pconn; char *id; @@ -257,6 +270,7 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBus pconn = pa_xnew(pa_dbus_wrap_connection, 1); pconn->mainloop = m; pconn->connection = conn; + pconn->use_rtclock = use_rtclock; dbus_connection_set_exit_on_disconnect(conn, FALSE); dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL); diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 55cda7a0..9ff298d8 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -30,7 +30,7 @@ /* A wrap connection is not shared or refcounted, it is available in client side */ typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; -pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error); +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusBusType type, DBusError *error); void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn); DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn); diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c index 4388831a..fc8ce76f 100644 --- a/src/pulsecore/pdispatch.c +++ b/src/pulsecore/pdispatch.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #include "pdispatch.h" @@ -204,6 +206,7 @@ struct pa_pdispatch { pa_pdispatch_drain_callback drain_callback; void *drain_userdata; const pa_creds *creds; + pa_bool_t use_rtclock:1; }; static void reply_info_free(struct reply_info *r) { @@ -220,7 +223,7 @@ static void reply_info_free(struct reply_info *r) { pa_xfree(r); } -pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) { +pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, const pa_pdispatch_cb_t *table, unsigned entries) { pa_pdispatch *pd; pa_assert(mainloop); @@ -235,6 +238,7 @@ pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_ pd->drain_callback = NULL; pd->drain_userdata = NULL; pd->creds = NULL; + pd->use_rtclock = use_rtclock; return pd; } @@ -342,7 +346,7 @@ finish: return ret; } -static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata) { +static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *t, void *userdata) { struct reply_info*r = userdata; pa_assert(r); @@ -371,10 +375,7 @@ void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa r->free_cb = free_cb; r->tag = tag; - pa_gettimeofday(&tv); - tv.tv_sec += timeout; - - pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r)); + pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + timeout * PA_USEC_PER_SEC, pd->use_rtclock), timeout_callback, r)); PA_LLIST_PREPEND(struct reply_info, pd->replies, r); } diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h index 5c31d80e..dae475af 100644 --- a/src/pulsecore/pdispatch.h +++ b/src/pulsecore/pdispatch.h @@ -37,7 +37,7 @@ typedef struct pa_pdispatch pa_pdispatch; typedef void (*pa_pdispatch_cb_t)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata); -pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, const pa_pdispatch_cb_t*table, unsigned entries); +pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, pa_bool_t use_rtclock, const pa_pdispatch_cb_t*table, unsigned entries); void pa_pdispatch_unref(pa_pdispatch *pd); pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd); diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index ad7cd045..f64552aa 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -63,7 +64,7 @@ #define MAX_CONNECTIONS 64 /* Kick a client if it doesn't authenticate within this time */ -#define AUTH_TIMEOUT 5 +#define AUTH_TIMEOUT (5*PA_USEC_PER_SEC) #define DEFAULT_COOKIE_FILE ".esd_auth" @@ -1459,11 +1460,10 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { /*** entry points ***/ -static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { +static void auth_timeout(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { connection *c = CONNECTION(userdata); pa_assert(m); - pa_assert(tv); connection_assert_ref(c); pa_assert(c->auth_timeout_event == e); @@ -1553,12 +1553,9 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou c->authorized = TRUE; } - if (!c->authorized) { - struct timeval tv; - pa_gettimeofday(&tv); - tv.tv_sec += AUTH_TIMEOUT; - c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c); - } else + if (!c->authorized) + c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c); + else c->auth_timeout_event = NULL; c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 48f7b135..cda7ef57 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -61,7 +62,7 @@ #include "protocol-native.h" /* Kick a client if it doesn't authenticate within this time */ -#define AUTH_TIMEOUT 60 +#define AUTH_TIMEOUT (60 * PA_USEC_PER_SEC) /* Don't accept more connection than this */ #define MAX_CONNECTIONS 64 @@ -4479,11 +4480,10 @@ static void client_send_event_cb(pa_client *client, const char*event, pa_proplis /*** module entry points ***/ -static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { +static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *t, void *userdata) { pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); pa_assert(m); - pa_assert(tv); pa_native_connection_assert_ref(c); pa_assert(c->auth_timeout_event == e); @@ -4541,12 +4541,9 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati c->authorized = TRUE; } - if (!c->authorized) { - struct timeval tv; - pa_gettimeofday(&tv); - tv.tv_sec += AUTH_TIMEOUT; - c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c); - } else + if (!c->authorized) + c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c); + else c->auth_timeout_event = NULL; c->is_local = pa_iochannel_socket_is_local(io); @@ -4565,7 +4562,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c); pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c); - c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX); + c->pdispatch = pa_pdispatch_new(p->core->mainloop, TRUE, command_table, PA_COMMAND_MAX); c->record_streams = pa_idxset_new(NULL, NULL); c->output_streams = pa_idxset_new(NULL, NULL); diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c index dc23bff6..24535157 100644 --- a/src/pulsecore/socket-client.c +++ b/src/pulsecore/socket-client.c @@ -52,12 +52,14 @@ #include #endif +#include #include #include #include #include #include +#include #include #include #include @@ -420,12 +422,11 @@ fail: #endif -static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) { +static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { pa_socket_client *c = userdata; pa_assert(m); pa_assert(e); - pa_assert(tv); pa_assert(c); if (c->fd >= 0) { @@ -437,17 +438,16 @@ static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeva do_call(c); } -static void start_timeout(pa_socket_client *c) { +static void start_timeout(pa_socket_client *c, pa_bool_t use_rtclock) { struct timeval tv; + pa_assert(c); pa_assert(!c->timeout_event); - pa_gettimeofday(&tv); - pa_timeval_add(&tv, CONNECT_TIMEOUT * PA_USEC_PER_SEC); - c->timeout_event = c->mainloop->time_new(c->mainloop, &tv, timeout_cb, c); + c->timeout_event = c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + CONNECT_TIMEOUT * PA_USEC_PER_SEC, use_rtclock), timeout_cb, c); } -pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*name, uint16_t default_port) { +pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, pa_bool_t use_rtclock, const char*name, uint16_t default_port) { pa_socket_client *c = NULL; pa_parsed_address a; @@ -463,7 +463,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam switch (a.type) { case PA_PARSED_ADDRESS_UNIX: if ((c = pa_socket_client_new_unix(m, a.path_or_host))) - start_timeout(c); + start_timeout(c, use_rtclock); break; case PA_PARSED_ADDRESS_TCP4: /* Fallthrough */ @@ -499,7 +499,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c); c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints); pa_assert(c->asyncns_query); - start_timeout(c); + start_timeout(c, use_rtclock); } #elif defined(HAVE_GETADDRINFO) { @@ -513,7 +513,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam if (res->ai_addr) { if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen))) - start_timeout(c); + start_timeout(c, use_rtclock); } freeaddrinfo(res); @@ -546,7 +546,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam s.sin_port = htons(a.port); if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s)))) - start_timeout(c); + start_timeout(c, use_rtclock); } #endif /* HAVE_LIBASYNCNS */ } diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h index ed36400c..b896afa9 100644 --- a/src/pulsecore/socket-client.h +++ b/src/pulsecore/socket-client.h @@ -40,7 +40,7 @@ pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[ #endif pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename); pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen); -pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char *a, uint16_t default_port); +pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, pa_bool_t use_rtclock, const char *a, uint16_t default_port); pa_socket_client* pa_socket_client_ref(pa_socket_client *c); void pa_socket_client_unref(pa_socket_client *c); diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c index 4ca63513..55331d12 100644 --- a/src/tests/mainloop-test.c +++ b/src/tests/mainloop-test.c @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -99,9 +100,7 @@ int main(int argc, char *argv[]) { de = a->defer_new(a, dcb, NULL); assert(de); - pa_gettimeofday(&tv); - tv.tv_sec += 10; - te = a->time_new(a, &tv, tcb, NULL); + te = a->time_new(a, pa_timeval_rtstore(&tv, pa_rtclock_now() + 2 * PA_USEC_PER_SEC, TRUE), tcb, NULL); #if defined(GLIB_MAIN_LOOP) g_main_loop_run(glib_main_loop); diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c index ad89414f..e3160f30 100644 --- a/src/tests/thread-mainloop-test.c +++ b/src/tests/thread-mainloop-test.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -32,7 +33,7 @@ #include -static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) { +static void tcb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) { pa_assert_se(pa_threaded_mainloop_in_thread(userdata)); fprintf(stderr, "TIME EVENT START\n"); pa_threaded_mainloop_signal(userdata, 1); @@ -53,9 +54,7 @@ int main(int argc, char *argv[]) { pa_assert_se(!pa_threaded_mainloop_in_thread(m)); - pa_gettimeofday(&tv); - tv.tv_sec += 5; - a->time_new(a, &tv, tcb, m); + a->time_new(a, pa_timeval_rtstore(&tv, pa_rtclock_now() + 5 * PA_USEC_PER_SEC, TRUE), tcb, m); fprintf(stderr, "waiting 5s (signal)\n"); pa_threaded_mainloop_wait(m); diff --git a/src/utils/pabrowse.c b/src/utils/pabrowse.c index a6487b88..a349e414 100644 --- a/src/utils/pabrowse.c +++ b/src/utils/pabrowse.c @@ -27,8 +27,9 @@ #include #include -#include #include +#include +#include #include diff --git a/src/utils/pacat.c b/src/utils/pacat.c index 0b6df3d8..f00a32eb 100644 --- a/src/utils/pacat.c +++ b/src/utils/pacat.c @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -583,9 +584,7 @@ static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int s pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)); } -static void time_event_callback(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { - struct timeval next; - +static void time_event_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) { pa_operation *o; if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL))) @@ -594,10 +593,7 @@ static void time_event_callback(pa_mainloop_api*m, pa_time_event *e, const struc pa_operation_unref(o); } - pa_gettimeofday(&next); - pa_timeval_add(&next, TIME_EVENT_USEC); - - m->time_restart(e, &next); + pa_context_rttime_restart(context, e, pa_rtclock_now() + TIME_EVENT_USEC); } static void help(const char *argv0) { @@ -1068,13 +1064,8 @@ int main(int argc, char *argv[]) { } if (verbose) { - struct timeval tv; - - pa_gettimeofday(&tv); - pa_timeval_add(&tv, TIME_EVENT_USEC); - - if (!(time_event = mainloop_api->time_new(mainloop_api, &tv, time_event_callback, NULL))) { - pa_log(_("time_new() failed.\n")); + if (!(time_event = pa_context_rttime_new(context, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL))) { + pa_log(_("pa_context_rttime_new() failed.\n")); goto quit; } } -- cgit From e4d914c945c13d23b131d7ba75fbdd03cb6d0043 Mon Sep 17 00:00:00 2001 From: Marc-AndrĂ© Lureau Date: Sat, 20 Jun 2009 16:52:41 +0300 Subject: rtclock: fix issues found by Lennart --- src/pulse/context.c | 14 +++++++++++--- src/pulse/mainloop.c | 20 +++++++++++--------- src/pulsecore/core-rtclock.c | 3 +++ src/tests/mainloop-test.c | 1 + 4 files changed, 26 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/pulse/context.c b/src/pulse/context.c index 0c1810f3..b71659de 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -54,6 +54,8 @@ #include #include #include +#include +#include #include #include @@ -1451,6 +1453,9 @@ pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_even pa_assert(c); pa_assert(c->mainloop); + if (usec == PA_USEC_INVALID) + return c->mainloop->time_new(c->mainloop, NULL, cb, userdata); + pa_timeval_rtstore(&tv, usec, c->use_rtclock); return c->mainloop->time_new(c->mainloop, &tv, cb, userdata); @@ -1462,7 +1467,10 @@ void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) pa_assert(c); pa_assert(c->mainloop); - pa_timeval_rtstore(&tv, usec, c->use_rtclock); - - c->mainloop->time_restart(e, &tv); + if (usec == PA_USEC_INVALID) + c->mainloop->time_restart(e, NULL); + else { + pa_timeval_rtstore(&tv, usec, c->use_rtclock); + c->mainloop->time_restart(e, &tv); + } } diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 1b779468..5d0e0ffc 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -319,19 +319,21 @@ static void mainloop_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy } /* Time events */ -static pa_usec_t timeval_load(struct timeval *tv) { +static pa_usec_t timeval_load(const struct timeval *tv) { pa_bool_t is_rtclock; + struct timeval ttv; if (!tv) return PA_USEC_INVALID; - is_rtclock = !!(tv->tv_usec & PA_TIMEVAL_RTCLOCK); - tv->tv_usec &= ~PA_TIMEVAL_RTCLOCK; + ttv = *tv; + is_rtclock = !!(ttv.tv_usec & PA_TIMEVAL_RTCLOCK); + ttv.tv_usec &= ~PA_TIMEVAL_RTCLOCK; if (!is_rtclock) - pa_rtclock_from_wallclock(tv); + pa_rtclock_from_wallclock(&ttv); - return pa_timeval_load(tv); + return pa_timeval_load(&ttv); } static pa_time_event* mainloop_time_new( @@ -343,13 +345,13 @@ static pa_time_event* mainloop_time_new( pa_mainloop *m; pa_time_event *e; pa_usec_t t; - struct timeval ttv; pa_assert(a); pa_assert(a->userdata); pa_assert(callback); - t = timeval_load(tv? ttv = *tv, &ttv : NULL); + t = timeval_load(tv); + m = a->userdata; pa_assert(a == &m->api); @@ -385,12 +387,12 @@ static pa_time_event* mainloop_time_new( static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { pa_bool_t valid; pa_usec_t t; - struct timeval ttv; pa_assert(e); pa_assert(!e->dead); - t = timeval_load(tv? ttv = *tv, &ttv : NULL); + t = timeval_load(tv); + valid = (t != PA_USEC_INVALID); if (e->enabled && !valid) { pa_assert(e->mainloop->n_enabled_time_events > 0); diff --git a/src/pulsecore/core-rtclock.c b/src/pulsecore/core-rtclock.c index 1fa71c80..a4a70bee 100644 --- a/src/pulsecore/core-rtclock.c +++ b/src/pulsecore/core-rtclock.c @@ -176,6 +176,9 @@ static struct timeval* wallclock_from_rtclock(struct timeval *tv) { struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, pa_bool_t rtclock) { pa_assert(tv); + if (v == PA_USEC_INVALID) + return NULL; + pa_timeval_store(tv, v); if (rtclock) diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c index 55331d12..3ec6d115 100644 --- a/src/tests/mainloop-test.c +++ b/src/tests/mainloop-test.c @@ -31,6 +31,7 @@ #include #include +#include #ifdef GLIB_MAIN_LOOP -- cgit From c4d90ea98617026fbeb9f8bc11e98ab528338274 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Jun 2009 22:33:09 +0200 Subject: restore: change 'save' flag behaviour to reflect whether an entry shall and/or is in the on-disk databases --- src/modules/module-card-restore.c | 2 +- src/modules/module-device-restore.c | 30 ++++++++++++++++++------------ src/modules/module-rescue-streams.c | 6 +++--- src/modules/module-stream-restore.c | 34 +++++++++++++++++----------------- 4 files changed, 39 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c index 85583b2c..ec55371c 100644 --- a/src/modules/module-card-restore.c +++ b/src/modules/module-card-restore.c @@ -199,7 +199,7 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new if (!new_data->active_profile) { pa_log_info("Restoring profile for card %s.", new_data->name); pa_card_new_data_set_profile(new_data, e->profile); - new_data->save_profile = FALSE; + new_data->save_profile = TRUE; } else pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name); diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index a2745b88..3acbdc81 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -296,10 +296,10 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new char *name; struct entry *e; + pa_assert(c); pa_assert(new_data); - - if (!u->restore_port) - return PA_HOOK_OK; + pa_assert(u); + pa_assert(u->restore_port); name = pa_sprintf_malloc("sink:%s", new_data->name); @@ -309,7 +309,7 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new if (!new_data->active_port) { pa_log_info("Restoring port for sink %s.", name); pa_sink_new_data_set_port(new_data, e->port); - new_data->save_port = FALSE; + new_data->save_port = TRUE; } else pa_log_debug("Not restoring port for sink %s, because already set.", name); } @@ -326,7 +326,10 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data * char *name; struct entry *e; + pa_assert(c); pa_assert(new_data); + pa_assert(u); + pa_assert(u->restore_volume || u->restore_muted); name = pa_sprintf_malloc("sink:%s", new_data->name); @@ -343,7 +346,7 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data * pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map); pa_sink_new_data_set_volume(new_data, &v); - new_data->save_volume = FALSE; + new_data->save_volume = TRUE; } else pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name); } @@ -353,7 +356,7 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data * if (!new_data->muted_is_set) { pa_log_info("Restoring mute state for sink %s.", new_data->name); pa_sink_new_data_set_muted(new_data, e->muted); - new_data->save_muted = FALSE; + new_data->save_muted = TRUE; } else pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name); } @@ -370,10 +373,10 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data char *name; struct entry *e; + pa_assert(c); pa_assert(new_data); - - if (!u->restore_port) - return PA_HOOK_OK; + pa_assert(u); + pa_assert(u->restore_port); name = pa_sprintf_malloc("source:%s", new_data->name); @@ -383,7 +386,7 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data if (!new_data->active_port) { pa_log_info("Restoring port for source %s.", name); pa_source_new_data_set_port(new_data, e->port); - new_data->save_port = FALSE; + new_data->save_port = TRUE; } else pa_log_debug("Not restoring port for source %s, because already set.", name); } @@ -400,7 +403,10 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da char *name; struct entry *e; + pa_assert(c); pa_assert(new_data); + pa_assert(u); + pa_assert(u->restore_volume || u->restore_muted); name = pa_sprintf_malloc("source:%s", new_data->name); @@ -417,7 +423,7 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map); pa_source_new_data_set_volume(new_data, &v); - new_data->save_volume = FALSE; + new_data->save_volume = TRUE; } else pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name); } @@ -427,7 +433,7 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da if (!new_data->muted_is_set) { pa_log_info("Restoring mute state for source %s.", new_data->name); pa_source_new_data_set_muted(new_data, e->muted); - new_data->save_muted = FALSE; + new_data->save_muted = TRUE; } else pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name); } diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c index c22711ae..f0b07eb5 100644 --- a/src/modules/module-rescue-streams.c +++ b/src/modules/module-rescue-streams.c @@ -65,14 +65,14 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user return PA_HOOK_OK; } - if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)) || target == sink) { + if (!(target = pa_namereg_get_default_sink(c)) || target == sink) { PA_IDXSET_FOREACH(target, c->sinks, idx) if (target != sink) break; if (!target) { - pa_log_info("No evacuation sink found."); + pa_log_debug("No evacuation sink found."); return PA_HOOK_OK; } } @@ -108,7 +108,7 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void return PA_HOOK_OK; } - if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE)) || target == source) { + if (!(target = pa_namereg_get_default_source(c)) || target == source) { PA_IDXSET_FOREACH(target, c->sources, idx) if (target != source && !target->monitor_of == !source->monitor_of) diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index f2aea27b..b519bc17 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -353,10 +353,10 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n char *name; struct entry *e; + pa_assert(c); pa_assert(new_data); - - if (!u->restore_device) - return PA_HOOK_OK; + pa_assert(u); + pa_assert(u->restore_device); if (!(name = get_name(new_data->proplist, "sink-input"))) return PA_HOOK_OK; @@ -370,9 +370,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n if (!new_data->sink) { pa_log_info("Restoring device for stream %s.", name); new_data->sink = s; - new_data->save_sink = FALSE; + new_data->save_sink = TRUE; } else - pa_log_info("Not restoring device for stream %s, because already set.", name); + pa_log_debug("Not restoring device for stream %s, because already set.", name); } } @@ -388,10 +388,10 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu char *name; struct entry *e; + pa_assert(c); pa_assert(new_data); - - if (!u->restore_volume && !u->restore_muted) - return PA_HOOK_OK; + pa_assert(u); + pa_assert(u->restore_volume || u->restore_muted); if (!(name = get_name(new_data->proplist, "sink-input"))) return PA_HOOK_OK; @@ -410,7 +410,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu pa_sink_input_new_data_set_volume(new_data, &v); new_data->volume_is_absolute = FALSE; - new_data->save_volume = FALSE; + new_data->save_volume = TRUE; } else pa_log_debug("Not restoring volume for sink input %s, because already set.", name); } @@ -420,7 +420,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu if (!new_data->muted_is_set) { pa_log_info("Restoring mute state for sink input %s.", name); pa_sink_input_new_data_set_muted(new_data, e->muted); - new_data->save_muted = FALSE; + new_data->save_muted = TRUE; } else pa_log_debug("Not restoring mute state for sink input %s, because already set.", name); } @@ -437,10 +437,10 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou char *name; struct entry *e; + pa_assert(c); pa_assert(new_data); - - if (!u->restore_device) - return PA_HOOK_OK; + pa_assert(u); + pa_assert(u->restore_device); if (new_data->direct_on_input) return PA_HOOK_OK; @@ -456,9 +456,9 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou if (!new_data->source) { pa_log_info("Restoring device for stream %s.", name); new_data->source = s; - new_data->save_source = FALSE; + new_data->save_source = TRUE; } else - pa_log_info("Not restoring device for stream %s, because already set", name); + pa_log_debug("Not restoring device for stream %s, because already set", name); } } @@ -829,10 +829,10 @@ int pa__init(pa_module*m) { pa_log_info("Sucessfully opened database file '%s'.", fname); pa_xfree(fname); - for (si = pa_idxset_first(m->core->sink_inputs, &idx); si; si = pa_idxset_next(m->core->sink_inputs, &idx)) + PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx) subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u); - for (so = pa_idxset_first(m->core->source_outputs, &idx); so; so = pa_idxset_next(m->core->source_outputs, &idx)) + PA_IDXSET_FOREACH(so, m->core->source_outputs, idx) subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u); pa_modargs_free(ma); -- cgit From 27af46045bfee03a8662e4443857e5d4623616e7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Jun 2009 22:34:57 +0200 Subject: modules: add module-intended-roles that automatically puts streams marked with a role on devices that are intended for that role --- src/Makefile.am | 8 + src/modules/module-intended-roles.c | 439 ++++++++++++++++++++++++++++++++++++ 2 files changed, 447 insertions(+) create mode 100644 src/modules/module-intended-roles.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index f3099e26..d643e2b9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -974,6 +974,7 @@ modlibexec_LTLIBRARIES += \ module-default-device-restore.la \ module-always-sink.la \ module-rescue-streams.la \ + module-intended-roles.la \ module-suspend-on-idle.la \ module-http-protocol-tcp.la \ module-sine.la \ @@ -1206,6 +1207,7 @@ SYMDEF_FILES = \ modules/module-default-device-restore-symdef.h \ modules/module-always-sink-symdef.h \ modules/module-rescue-streams-symdef.h \ + modules/module-intended-roles-symdef.h \ modules/module-suspend-on-idle-symdef.h \ modules/module-hal-detect-symdef.h \ modules/module-udev-detect-symdef.h \ @@ -1538,6 +1540,12 @@ module_rescue_streams_la_LDFLAGS = $(MODULE_LDFLAGS) module_rescue_streams_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_rescue_streams_la_CFLAGS = $(AM_CFLAGS) +# Automatically move streams to devices that are intended for their roles +module_intended_roles_la_SOURCES = modules/module-intended-roles.c +module_intended_roles_la_LDFLAGS = $(MODULE_LDFLAGS) +module_intended_roles_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_intended_roles_la_CFLAGS = $(AM_CFLAGS) + # Suspend-on-idle module module_suspend_on_idle_la_SOURCES = modules/module-suspend-on-idle.c module_suspend_on_idle_la_LDFLAGS = $(MODULE_LDFLAGS) diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c new file mode 100644 index 00000000..036600e4 --- /dev/null +++ b/src/modules/module-intended-roles.c @@ -0,0 +1,439 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-stream-restore-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("Automatically set device of streams based of intended roles of devices"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_USAGE( + "on_hotplug= " + "on_rescue="); + +static const char* const valid_modargs[] = { + "on_hotplug", + "on_rescue", + NULL +}; + +struct userdata { + pa_core *core; + pa_module *module; + pa_hook_slot + *sink_input_new_hook_slot, + *source_output_new_hook_slot, + *sink_put_hook_slot, + *source_put_hook_slot, + *sink_unlink_hook_slot, + *source_unlink_hook_slot; + + pa_bool_t on_hotplug:1; + pa_bool_t on_rescue:1; +}; + +static pa_bool_t role_match(pa_proplist *proplist, const char *role) { + const char *ir; + char *r; + const char *state; + + if (!(ir = pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES))) + return FALSE; + + while ((r = pa_split_spaces(ir, &state))) { + + if (pa_streq(role, r)) { + pa_xfree(r); + return TRUE; + } + + pa_xfree(r); + } + + return FALSE; +} + +static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) { + const char *role; + pa_sink *s, *def; + uint32_t idx; + + pa_assert(c); + pa_assert(new_data); + pa_assert(u); + + if (!new_data->proplist) { + pa_log_debug("New stream lacks property data."); + return PA_HOOK_OK; + } + + if (new_data->sink) { + pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME))); + return PA_HOOK_OK; + } + + if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) { + pa_log_debug("Not setting device for stream %s, because it lacks role.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME))); + return PA_HOOK_OK; + } + + /* Prefer the default sink over any other sink, just in case... */ + if ((def = pa_namereg_get_default_sink(c))) + if (role_match(def->proplist, role)) { + new_data->sink = def; + new_data->save_sink = FALSE; + return PA_HOOK_OK; + } + + PA_IDXSET_FOREACH(s, c->sinks, idx) { + if (s == def) + continue; + + if (role_match(s->proplist, role)) { + new_data->sink = s; + new_data->save_sink = FALSE; + return PA_HOOK_OK; + } + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) { + const char *role; + pa_source *s, *def; + uint32_t idx; + + pa_assert(c); + pa_assert(new_data); + pa_assert(u); + + if (!new_data->proplist) { + pa_log_debug("New stream lacks property data."); + return PA_HOOK_OK; + } + + if (new_data->source) { + pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME))); + return PA_HOOK_OK; + } + + if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) { + pa_log_debug("Not setting device for stream %s, because it lacks role.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME))); + return PA_HOOK_OK; + } + + /* Prefer the default source over any other source, just in case... */ + if ((def = pa_namereg_get_default_source(c))) + if (role_match(def->proplist, role)) { + new_data->source = def; + new_data->save_source = FALSE; + return PA_HOOK_OK; + } + + PA_IDXSET_FOREACH(s, c->sources, idx) { + if (s == def) + continue; + + if (role_match(s->proplist, role)) { + new_data->source = s; + new_data->save_source = FALSE; + return PA_HOOK_OK; + } + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { + pa_sink_input *si; + uint32_t idx; + + pa_assert(c); + pa_assert(sink); + pa_assert(u); + pa_assert(u->on_hotplug); + + PA_IDXSET_FOREACH(si, c->sink_inputs, idx) { + const char *role; + + if (si->sink == sink) + continue; + + if (si->save_sink) + continue; + + if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) + continue; + + if (role_match(si->sink->proplist, role)) + continue; + + if (!role_match(sink->proplist, role)) + continue; + + pa_sink_input_move_to(si, sink, FALSE); + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { + pa_source_output *so; + uint32_t idx; + + pa_assert(c); + pa_assert(source); + pa_assert(u); + pa_assert(u->on_hotplug); + + PA_IDXSET_FOREACH(so, c->source_outputs, idx) { + const char *role; + + if (so->source == source) + continue; + + if (so->save_source) + continue; + + if (so->direct_on_input) + continue; + + if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE))) + continue; + + if (role_match(so->source->proplist, role)) + continue; + + if (!role_match(source->proplist, role)) + continue; + + pa_source_output_move_to(so, source, FALSE); + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { + pa_sink_input *si; + uint32_t idx; + pa_sink *def; + + pa_assert(c); + pa_assert(sink); + pa_assert(u); + pa_assert(u->on_rescue); + + /* There's no point in doing anything if the core is shut down anyway */ + if (c->state == PA_CORE_SHUTDOWN) + return PA_HOOK_OK; + + /* If there not default sink, then there is no sink at all */ + if (!(def = pa_namereg_get_default_sink(c))) + return PA_HOOK_OK; + + PA_IDXSET_FOREACH(si, sink->inputs, idx) { + const char *role; + uint32_t jdx; + pa_sink *d; + + if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) + continue; + + /* Would the default sink fit? If so, let's use it */ + if (def != sink && role_match(def->proplist, role)) { + pa_sink_input_move_to(si, def, FALSE); + continue; + } + + /* Try to find some other fitting sink */ + PA_IDXSET_FOREACH(d, c->sinks, jdx) { + if (d == def || d == sink) + continue; + + if (role_match(d->proplist, role)) { + pa_sink_input_move_to(si, d, FALSE); + break; + } + } + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { + pa_source_output *so; + uint32_t idx; + pa_source *def; + + pa_assert(c); + pa_assert(source); + pa_assert(u); + pa_assert(u->on_rescue); + + /* There's no point in doing anything if the core is shut down anyway */ + if (c->state == PA_CORE_SHUTDOWN) + return PA_HOOK_OK; + + /* If there not default source, then there is no source at all */ + if (!(def = pa_namereg_get_default_source(c))) + return PA_HOOK_OK; + + PA_IDXSET_FOREACH(so, source->outputs, idx) { + const char *role; + uint32_t jdx; + pa_source *d; + + if (so->direct_on_input) + continue; + + if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE))) + continue; + + /* Would the default source fit? If so, let's use it */ + if (def != source && role_match(def->proplist, role) && !source->monitor_of == !def->monitor_of) { + pa_source_output_move_to(so, def, FALSE); + continue; + } + + /* Try to find some other fitting source */ + PA_IDXSET_FOREACH(d, c->sources, jdx) { + if (d == def || d == source) + continue; + + if (role_match(d->proplist, role) && !source->monitor_of == !d->monitor_of) { + pa_source_output_move_to(so, d, FALSE); + break; + } + } + } + + return PA_HOOK_OK; +} + +int pa__init(pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u; + pa_bool_t on_hotplug = TRUE, on_rescue = TRUE; + + 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, "on_hotplug", &on_hotplug) < 0 || + pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) { + pa_log("on_hotplug= and on_rescue= expect boolean arguments"); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->on_hotplug = on_hotplug; + u->on_rescue = on_rescue; + + /* A little bit later than module-stream-restore */ + u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) sink_input_new_hook_callback, u); + u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) source_output_new_hook_callback, u); + + if (on_hotplug) { + /* A little bit later than module-stream-restore */ + u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_put_hook_callback, u); + u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) source_put_hook_callback, u); + } + + if (on_rescue) { + /* A little bit later than module-stream-restore, a little bit earlier than module-rescue-streams, ... */ + u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_unlink_hook_callback, NULL); + u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) source_unlink_hook_callback, NULL); + } + + pa_modargs_free(ma); + return 0; + +fail: + pa__done(m); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa__done(pa_module*m) { + struct userdata* u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->sink_input_new_hook_slot) + pa_hook_slot_free(u->sink_input_new_hook_slot); + if (u->source_output_new_hook_slot) + pa_hook_slot_free(u->source_output_new_hook_slot); + + if (u->sink_put_hook_slot) + pa_hook_slot_free(u->sink_put_hook_slot); + if (u->source_put_hook_slot) + pa_hook_slot_free(u->source_put_hook_slot); + + if (u->sink_unlink_hook_slot) + pa_hook_slot_free(u->sink_unlink_hook_slot); + if (u->source_unlink_hook_slot) + pa_hook_slot_free(u->source_unlink_hook_slot); + + pa_xfree(u); +} -- cgit From 60d36c7e74204bb8a479272c870736017e102c58 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Jun 2009 22:35:55 +0200 Subject: module-stream-restore: recheck stream database on hotplug/unplug and potentially move streams --- src/modules/module-stream-restore.c | 194 +++++++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index b519bc17..bf100e2d 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -59,7 +59,9 @@ PA_MODULE_LOAD_ONCE(TRUE); PA_MODULE_USAGE( "restore_device= " "restore_volume= " - "restore_muted="); + "restore_muted= " + "on_hotplug= " + "on_rescue="); #define SAVE_INTERVAL 10 #define IDENTIFICATION_PROPERTY "module-stream-restore.id" @@ -68,6 +70,8 @@ static const char* const valid_modargs[] = { "restore_device", "restore_volume", "restore_muted", + "on_hotplug", + "on_rescue", NULL }; @@ -79,6 +83,10 @@ struct userdata { *sink_input_new_hook_slot, *sink_input_fixate_hook_slot, *source_output_new_hook_slot, + *sink_put_hook_slot, + *source_put_hook_slot, + *sink_unlink_hook_slot, + *source_unlink_hook_slot, *connection_unlink_hook_slot; pa_time_event *save_time_event; pa_database* database; @@ -86,6 +94,8 @@ struct userdata { pa_bool_t restore_device:1; pa_bool_t restore_volume:1; pa_bool_t restore_muted:1; + pa_bool_t on_hotplug:1; + pa_bool_t on_rescue:1; pa_native_protocol *protocol; pa_idxset *subscribed; @@ -470,6 +480,155 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou return PA_HOOK_OK; } +static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { + pa_sink_input *si; + uint32_t idx; + + pa_assert(c); + pa_assert(sink); + pa_assert(u); + pa_assert(u->on_hotplug && u->restore_device); + + PA_IDXSET_FOREACH(si, c->sink_inputs, idx) { + char *name; + struct entry *e; + + if (si->sink == sink) + continue; + + if (si->save_sink) + continue; + + if (!(name = get_name(si->proplist, "sink-input"))) + continue; + + if ((e = read_entry(u, name))) { + if (e->device_valid && pa_streq(e->device, sink->name)) + pa_sink_input_move_to(si, sink, TRUE); + + pa_xfree(e); + } + + pa_xfree(name); + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { + pa_source_output *so; + uint32_t idx; + + pa_assert(c); + pa_assert(source); + pa_assert(u); + pa_assert(u->on_hotplug && u->restore_device); + + PA_IDXSET_FOREACH(so, c->source_outputs, idx) { + char *name; + struct entry *e; + + if (so->source == source) + continue; + + if (so->save_source) + continue; + + if (so->direct_on_input) + continue; + + if (!(name = get_name(so->proplist, "source-input"))) + continue; + + if ((e = read_entry(u, name))) { + if (e->device_valid && pa_streq(e->device, source->name)) + pa_source_output_move_to(so, source, TRUE); + + pa_xfree(e); + } + + pa_xfree(name); + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { + pa_sink_input *si; + uint32_t idx; + + pa_assert(c); + pa_assert(sink); + pa_assert(u); + pa_assert(u->on_rescue && u->restore_device); + + /* There's no point in doing anything if the core is shut down anyway */ + if (c->state == PA_CORE_SHUTDOWN) + return PA_HOOK_OK; + + PA_IDXSET_FOREACH(si, sink->inputs, idx) { + char *name; + struct entry *e; + + if (!(name = get_name(si->proplist, "sink-input"))) + continue; + + if ((e = read_entry(u, name))) { + + if (e->device_valid) { + pa_sink *d; + + if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) && d != sink) + pa_sink_input_move_to(si, d, TRUE); + } + + pa_xfree(e); + } + + pa_xfree(name); + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { + pa_source_output *so; + uint32_t idx; + + pa_assert(c); + pa_assert(source); + pa_assert(u); + pa_assert(u->on_rescue && u->restore_device); + + /* There's no point in doing anything if the core is shut down anyway */ + if (c->state == PA_CORE_SHUTDOWN) + return PA_HOOK_OK; + + PA_IDXSET_FOREACH(so, source->outputs, idx) { + char *name; + struct entry *e; + + if (!(name = get_name(so->proplist, "source-output"))) + continue; + + if ((e = read_entry(u, name))) { + + if (e->device_valid) { + pa_source *d; + + if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) && d != source) + pa_source_output_move_to(so, d, TRUE); + } + + pa_xfree(e); + } + + pa_xfree(name); + } + + return PA_HOOK_OK; +} + #define EXT_VERSION 1 static void apply_entry(struct userdata *u, const char *name, struct entry *e) { @@ -775,7 +934,7 @@ int pa__init(pa_module*m) { pa_sink_input *si; pa_source_output *so; uint32_t idx; - pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE; + pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE, on_hotplug = TRUE, on_rescue = TRUE; pa_assert(m); @@ -786,8 +945,10 @@ int pa__init(pa_module*m) { if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 || pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 || - pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0) { - pa_log("restore_device=, restore_volume= and restore_muted= expect boolean arguments"); + pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 || + pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 || + pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) { + pa_log("restore_device=, restore_volume=, restore_muted=, on_hotplug= and on_rescue= expect boolean arguments"); goto fail; } @@ -800,6 +961,8 @@ int pa__init(pa_module*m) { u->restore_device = restore_device; u->restore_volume = restore_volume; u->restore_muted = restore_muted; + u->on_hotplug = on_hotplug; + u->on_rescue = on_rescue; u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); u->protocol = pa_native_protocol_get(m->core); @@ -810,10 +973,23 @@ int pa__init(pa_module*m) { u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u); if (restore_device) { + /* A little bit earlier than module-intended-roles ... */ u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u); u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u); } + if (restore_device && on_hotplug) { + /* A little bit earlier than module-intended-roles ... */ + u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_callback, u); + u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) source_put_hook_callback, u); + } + + if (restore_device && on_rescue) { + /* A little bit earlier than module-intended-roles, module-rescue-streams, ... */ + u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_unlink_hook_callback, NULL); + u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_unlink_hook_callback, NULL); + } + if (restore_volume || restore_muted) u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u); @@ -865,6 +1041,16 @@ void pa__done(pa_module*m) { if (u->source_output_new_hook_slot) pa_hook_slot_free(u->source_output_new_hook_slot); + if (u->sink_put_hook_slot) + pa_hook_slot_free(u->sink_put_hook_slot); + if (u->source_put_hook_slot) + pa_hook_slot_free(u->source_put_hook_slot); + + if (u->sink_unlink_hook_slot) + pa_hook_slot_free(u->sink_unlink_hook_slot); + if (u->source_unlink_hook_slot) + pa_hook_slot_free(u->source_unlink_hook_slot); + if (u->connection_unlink_hook_slot) pa_hook_slot_free(u->connection_unlink_hook_slot); -- cgit From aa2570c2ba8f1ff3cbbcb56607f0d013351916ff Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Jun 2009 22:36:23 +0200 Subject: rescue: make sure module-rescue-streams is used only as last fallback --- src/modules/module-rescue-streams.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c index f0b07eb5..e933cc2e 100644 --- a/src/modules/module-rescue-streams.c +++ b/src/modules/module-rescue-streams.c @@ -146,8 +146,10 @@ int pa__init(pa_module*m) { } m->userdata = u = pa_xnew(struct userdata, 1); - u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_hook_callback, NULL); - u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_hook_callback, NULL); + + /* A little bit later than module-stream-restore, module-intended-roles... */ + u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_hook_callback, NULL); + u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_hook_callback, NULL); pa_modargs_free(ma); return 0; -- cgit From de5219aafba6474f7a894dad8559976e5ea3144f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Jun 2009 22:44:29 +0200 Subject: daemon: enable module-intended-roles by default --- src/daemon/default.pa.in | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in index a35ff8ff..00c000eb 100755 --- a/src/daemon/default.pa.in +++ b/src/daemon/default.pa.in @@ -100,6 +100,9 @@ load-module module-rescue-streams ### Make sure we always have a sink around, even if it is a null sink. load-module module-always-sink +### Honour intended role device property +load-module module-intended-roles + ### Automatically suspend sinks/sources that become idle for too long load-module module-suspend-on-idle -- cgit From 3ee29004a9f5980b06647c5ba66a791f41390929 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Jun 2009 23:15:01 +0200 Subject: rtclock: fix build after merge --- src/daemon/main.c | 1 - src/tests/thread-mainloop-test.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/daemon/main.c b/src/daemon/main.c index 22759a38..b58bb379 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -85,7 +85,6 @@ #include #include #include -#include #include #include #include diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c index e3160f30..4696fb01 100644 --- a/src/tests/thread-mainloop-test.c +++ b/src/tests/thread-mainloop-test.c @@ -32,6 +32,7 @@ #include #include +#include static void tcb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) { pa_assert_se(pa_threaded_mainloop_in_thread(userdata)); -- cgit From f753ef2a23c0c42e5ad35a36327a9fda2e4f2d98 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Jun 2009 23:49:40 +0200 Subject: rtclock: enable rtclock for our own mainloop implementations --- src/pulse/context.c | 1 + src/pulse/internal.h | 2 ++ src/pulse/mainloop.c | 15 +++++++++++---- 3 files changed, 14 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/pulse/context.c b/src/pulse/context.c index b71659de..505e758a 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -160,6 +160,7 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * c->playback_streams = pa_dynarray_new(); c->record_streams = pa_dynarray_new(); c->client_index = PA_INVALID_INDEX; + c->use_rtclock = pa_mainloop_is_our_api(mainloop); PA_LLIST_HEAD_INIT(pa_stream, c->streams); PA_LLIST_HEAD_INIT(pa_operation, c->operations); diff --git a/src/pulse/internal.h b/src/pulse/internal.h index fa5efd7d..ec2da85b 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -280,4 +280,6 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t); +pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m); + #endif diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 5d0e0ffc..c418d108 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -56,6 +56,7 @@ #include #include "mainloop.h" +#include "internal.h" struct pa_io_event { pa_mainloop *mainloop; @@ -456,10 +457,10 @@ static void mainloop_quit(pa_mainloop_api*a, int retval) { static const pa_mainloop_api vtable = { .userdata = NULL, - .io_new= mainloop_io_new, - .io_enable= mainloop_io_enable, - .io_free= mainloop_io_free, - .io_set_destroy= mainloop_io_set_destroy, + .io_new = mainloop_io_new, + .io_enable = mainloop_io_enable, + .io_free = mainloop_io_free, + .io_set_destroy = mainloop_io_set_destroy, .time_new = mainloop_time_new, .time_restart = mainloop_time_restart, @@ -994,3 +995,9 @@ void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *use m->poll_func = poll_func; m->poll_func_userdata = userdata; } + +pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m) { + pa_assert(m); + + return m->io_new == mainloop_io_new; +} -- cgit From f3bbbd0fd377e856a27a2ed4f57ac042d620662f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Jun 2009 00:15:57 +0200 Subject: rtclock: document that we fallback to wallclock time if monotonic time is not supported --- src/pulse/rtclock.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulse/rtclock.h b/src/pulse/rtclock.h index eeea6251..6459d92d 100644 --- a/src/pulse/rtclock.h +++ b/src/pulse/rtclock.h @@ -27,11 +27,13 @@ #include /** \file - * Monotonic clock utility. */ + * Monotonic clock utilities. */ PA_C_DECL_BEGIN -/** Return the current CLOCK_MONOTONIC time in usec. \since 0.9.16 */ +/** Return the current monotonic system time in usec, if such a clock + * is available. If it is not available this will return the + * wallclock time instead. \since 0.9.16 */ pa_usec_t pa_rtclock_now(void); PA_C_DECL_END -- cgit From 9217b47b1920c0c88be6d1817f0479074b6328b8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Jun 2009 00:16:42 +0200 Subject: timeval: don't create the wrong illusion that nsecs should be stored in pa_usec_t --- src/pulse/timeval.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index 6307735c..48c6cdb3 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -40,16 +40,16 @@ PA_C_DECL_BEGIN #define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL) /** The number of nanoseconds in a second */ -#define PA_NSEC_PER_SEC ((pa_usec_t) 1000000000ULL) +#define PA_NSEC_PER_SEC ((unsigned long long) 1000000000ULL) /** The number of microseconds in a millisecond */ #define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL) /** The number of nanoseconds in a millisecond */ -#define PA_NSEC_PER_MSEC ((pa_usec_t) 1000000ULL) +#define PA_NSEC_PER_MSEC ((unsigned long long) 1000000ULL) /** The number of nanoseconds in a microsecond */ -#define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL) +#define PA_NSEC_PER_USEC ((unsigned long long) 1000ULL) /** Invalid time in usec */ #define PA_USEC_INVALID ((pa_usec_t) -1) -- cgit From 882cdfc123d89a393d237e130a6f8e213c91dc40 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Jun 2009 00:17:26 +0200 Subject: rtclock: fix type of PA_TIMEVAL_RTCLOCK --- src/pulsecore/core-rtclock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/pulsecore/core-rtclock.h b/src/pulsecore/core-rtclock.h index 3c90068b..9f5ae2dd 100644 --- a/src/pulsecore/core-rtclock.h +++ b/src/pulsecore/core-rtclock.h @@ -39,7 +39,7 @@ void pa_rtclock_hrtimer_enable(void); #define PA_HRTIMER_THRESHOLD_USEC 10 /* bit to set in tv.tv_usec to mark that the timeval is in monotonic time */ -#define PA_TIMEVAL_RTCLOCK (1 << 30) +#define PA_TIMEVAL_RTCLOCK ((time_t) (1LU << 30)) struct timeval* pa_rtclock_from_wallclock(struct timeval *tv); -- cgit From b627d68721854352c79facd30ccddeeb0b81cacf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Jun 2009 03:35:34 +0200 Subject: intended-roles: fix symdef header inclusion --- src/modules/module-intended-roles.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c index 036600e4..ce74a01c 100644 --- a/src/modules/module-intended-roles.c +++ b/src/modules/module-intended-roles.c @@ -50,7 +50,7 @@ #include #include -#include "module-stream-restore-symdef.h" +#include "module-intended-roles-symdef.h" PA_MODULE_AUTHOR("Lennart Poettering"); PA_MODULE_DESCRIPTION("Automatically set device of streams based of intended roles of devices"); -- cgit From d965000982060bc1c92555897828808ff5d928f5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Jun 2009 03:55:32 +0200 Subject: rtclock: make use of constants when converting between nsec and usec --- src/pulsecore/core-rtclock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/pulsecore/core-rtclock.c b/src/pulsecore/core-rtclock.c index a4a70bee..3b3e3a64 100644 --- a/src/pulsecore/core-rtclock.c +++ b/src/pulsecore/core-rtclock.c @@ -65,7 +65,7 @@ struct timeval *pa_rtclock_get(struct timeval *tv) { pa_assert(tv); tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / 1000; + tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC; return tv; @@ -82,11 +82,11 @@ pa_bool_t pa_rtclock_hrtimer(void) { #ifdef CLOCK_MONOTONIC if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0) - return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000; + return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC); #endif pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0); - return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000; + return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC); #else /* HAVE_CLOCK_GETTIME */ -- cgit From b6d0b0e808b76d74c73cae947c1a2dbf8f3b7c3c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Jun 2009 04:23:26 +0200 Subject: intended-roles: properly initialize split state --- src/modules/module-intended-roles.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c index ce74a01c..38b8f484 100644 --- a/src/modules/module-intended-roles.c +++ b/src/modules/module-intended-roles.c @@ -84,7 +84,7 @@ struct userdata { static pa_bool_t role_match(pa_proplist *proplist, const char *role) { const char *ir; char *r; - const char *state; + const char *state = NULL; if (!(ir = pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES))) return FALSE; -- cgit From ef85558abd380254086a81923fa4d35938e9921b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Jun 2009 04:25:45 +0200 Subject: modules: pass properly initialized userdata pointers to various hooks --- src/modules/module-intended-roles.c | 4 ++-- src/modules/module-rescue-streams.c | 4 ++-- src/modules/module-stream-restore.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c index 38b8f484..eca67c8f 100644 --- a/src/modules/module-intended-roles.c +++ b/src/modules/module-intended-roles.c @@ -396,8 +396,8 @@ int pa__init(pa_module*m) { if (on_rescue) { /* A little bit later than module-stream-restore, a little bit earlier than module-rescue-streams, ... */ - u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_unlink_hook_callback, NULL); - u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) source_unlink_hook_callback, NULL); + u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_unlink_hook_callback, u); + u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) source_unlink_hook_callback, u); } pa_modargs_free(ma); diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c index e933cc2e..c23feceb 100644 --- a/src/modules/module-rescue-streams.c +++ b/src/modules/module-rescue-streams.c @@ -148,8 +148,8 @@ int pa__init(pa_module*m) { m->userdata = u = pa_xnew(struct userdata, 1); /* A little bit later than module-stream-restore, module-intended-roles... */ - u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_hook_callback, NULL); - u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_hook_callback, NULL); + u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_hook_callback, u); + u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_hook_callback, u); pa_modargs_free(ma); return 0; diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 7ae93c7d..e60cc735 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -983,8 +983,8 @@ int pa__init(pa_module*m) { if (restore_device && on_rescue) { /* A little bit earlier than module-intended-roles, module-rescue-streams, ... */ - u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_unlink_hook_callback, NULL); - u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_unlink_hook_callback, NULL); + u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_unlink_hook_callback, u); + u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_unlink_hook_callback, u); } if (restore_volume || restore_muted) -- cgit From 5b78752a04053249f7543af772571815b5034ab5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Jun 2009 16:09:32 +0200 Subject: intended-roles: drop quite a few unnecessary includes --- src/modules/module-intended-roles.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'src') diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c index eca67c8f..c697209a 100644 --- a/src/modules/module-intended-roles.c +++ b/src/modules/module-intended-roles.c @@ -23,14 +23,6 @@ #include #endif -#include -#include -#include -#include -#include -#include -#include - #include #include #include @@ -45,10 +37,6 @@ #include #include #include -#include -#include -#include -#include #include "module-intended-roles-symdef.h" @@ -69,6 +57,7 @@ static const char* const valid_modargs[] = { struct userdata { pa_core *core; pa_module *module; + pa_hook_slot *sink_input_new_hook_slot, *source_output_new_hook_slot, -- cgit From 0b52bf4cd11e4191ea359fd8eebaf8d0b446acb8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Jun 2009 18:33:36 +0200 Subject: build-sys: don't make pulseaudio binary suid on install anymore --- src/Makefile.am | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index c580ece8..ae90ae8f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1696,7 +1696,6 @@ daemon.conf: daemon/daemon.conf.in Makefile install-exec-hook: chown root $(DESTDIR)$(bindir)/pulseaudio ; true - chmod u+s $(DESTDIR)$(bindir)/pulseaudio -chmod u+s $(DESTDIR)$(pulselibexecdir)/proximity-helper ln -sf pacat $(DESTDIR)$(bindir)/parec ln -sf pacat $(DESTDIR)$(bindir)/pamon -- cgit From 2654eb7781ddcfe53064745bbad77bffe5c8eada Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Jun 2009 18:54:17 +0200 Subject: sndfile: fix build on BE archs --- src/pulsecore/sndfile-util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/pulsecore/sndfile-util.c b/src/pulsecore/sndfile-util.c index 032aefca..4f7f8bdb 100644 --- a/src/pulsecore/sndfile-util.c +++ b/src/pulsecore/sndfile-util.c @@ -113,7 +113,7 @@ int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss) { break; case PA_SAMPLE_S32LE: - case PA_SAMPLE_S32RE: + case PA_SAMPLE_S32BE: ss->format = PA_SAMPLE_S32NE; sfi->format |= SF_FORMAT_PCM_32; break; -- cgit