implement device reservation scheme

This commit is contained in:
Lennart Poettering 2009-02-24 06:13:39 +01:00
parent 3c73025bf5
commit c341010304
10 changed files with 1102 additions and 2 deletions

View file

@ -1309,10 +1309,16 @@ libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
if HAVE_HAL
libalsa_util_la_SOURCES += modules/hal-util.h modules/hal-util.c
libalsa_util_la_LIBADD += $(HAL_LIBS) libdbus-util.la
libalsa_util_la_LIBADD += $(HAL_LIBS)
libalsa_util_la_CFLAGS += $(HAL_CFLAGS)
endif
if HAVE_DBUS
libalsa_util_la_SOURCES += modules/reserve.h modules/reserve.c modules/reserve-wrap.c modules/reserve-wrap.h
libalsa_util_la_LIBADD += $(DBUS_LIBS)
libalsa_util_la_CFLAGS += $(DBUS_CFLAGS)
endif
module_alsa_sink_la_SOURCES = modules/alsa/module-alsa-sink.c
module_alsa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
@ -1614,6 +1620,11 @@ update-sbc:
wget -O modules/bluetooth/$$i http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=audio/$$i ; \
done
update-reserve:
for i in reserve.c reserve.h ; do \
wget -O modules/$$i http://git.0pointer.de/\?p=reserve.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 {" ; \

View file

@ -53,6 +53,8 @@
#include <pulsecore/rtclock.h>
#include <pulsecore/time-smoother.h>
#include <modules/reserve-wrap.h>
#include "alsa-util.h"
#include "alsa-sink.h"
@ -101,10 +103,62 @@ struct userdata {
pa_smoother *smoother;
uint64_t write_count;
uint64_t since_start;
pa_reserve_wrapper *reserve;
pa_hook_slot *reserve_slot;
};
static void userdata_free(struct userdata *u);
static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct userdata *u) {
pa_assert(r);
pa_assert(u);
if (pa_sink_suspend(u->sink, TRUE) < 0)
return PA_HOOK_CANCEL;
return PA_HOOK_OK;
}
static void reserve_done(struct userdata *u) {
pa_assert(u);
if (u->reserve_slot) {
pa_hook_slot_free(u->reserve_slot);
u->reserve_slot = NULL;
}
if (u->reserve) {
pa_reserve_wrapper_unref(u->reserve);
u->reserve = NULL;
}
}
static int reserve_init(struct userdata *u, const char *dname) {
char *rname;
pa_assert(u);
pa_assert(dname);
if (u->reserve)
return 0;
/* We are resuming, try to lock the device */
if (!(rname = pa_alsa_get_reserve_name(dname)))
return 0;
u->reserve = pa_reserve_wrapper_get(u->core, rname);
pa_xfree(rname);
if (!(u->reserve))
return -1;
pa_assert(!u->reserve_slot);
u->reserve_slot = pa_hook_connect(pa_reserve_wrapper_hook(u->reserve), PA_HOOK_NORMAL, (pa_hook_cb_t) reserve_cb, u);
return 0;
}
static void fix_min_sleep_wakeup(struct userdata *u) {
size_t max_use, max_use_2;
@ -601,6 +655,7 @@ static int build_pollfd(struct userdata *u) {
return 0;
}
/* Called from IO context */
static int suspend(struct userdata *u) {
pa_assert(u);
pa_assert(u->pcm_handle);
@ -622,6 +677,7 @@ static int suspend(struct userdata *u) {
return 0;
}
/* Called from IO context */
static int update_sw_params(struct userdata *u) {
snd_pcm_uframes_t avail_min;
int err;
@ -677,6 +733,7 @@ static int update_sw_params(struct userdata *u) {
return 0;
}
/* Called from IO context */
static int unsuspend(struct userdata *u) {
pa_sample_spec ss;
int err;
@ -749,6 +806,7 @@ fail:
return -1;
}
/* Called from IO context */
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;
@ -804,6 +862,25 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return pa_sink_process_msg(o, code, data, offset, chunk);
}
/* Called from main context */
static int sink_set_state_cb(pa_sink *s, pa_sink_state_t new_state) {
pa_sink_state_t old_state;
struct userdata *u;
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
old_state = pa_sink_get_state(u->sink);
if (PA_SINK_IS_OPENED(old_state) && new_state == PA_SINK_SUSPENDED)
reserve_done(u);
else if (old_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(new_state))
if (reserve_init(u, u->device_name) < 0)
return -1;
return 0;
}
static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
struct userdata *u = snd_mixer_elem_get_callback_private(elem);
@ -1468,6 +1545,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_smoother_set_time_offset(u->smoother, usec);
pa_smoother_pause(u->smoother, usec);
if (reserve_init(u, pa_modargs_get_value(
ma, "device_id",
pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0)
goto fail;
b = use_mmap;
d = use_tsched;
@ -1569,6 +1651,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->userdata = u;
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
@ -1681,6 +1764,8 @@ static void userdata_free(struct userdata *u) {
if (u->smoother)
pa_smoother_free(u->smoother);
reserve_done(u);
pa_xfree(u->device_name);
pa_xfree(u);
}

View file

@ -54,6 +54,8 @@
#include <pulsecore/time-smoother.h>
#include <pulsecore/rtclock.h>
#include <modules/reserve-wrap.h>
#include "alsa-util.h"
#include "alsa-source.h"
@ -99,10 +101,62 @@ struct userdata {
pa_smoother *smoother;
uint64_t read_count;
pa_reserve_wrapper *reserve;
pa_hook_slot *reserve_slot;
};
static void userdata_free(struct userdata *u);
static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct userdata *u) {
pa_assert(r);
pa_assert(u);
if (pa_source_suspend(u->source, TRUE) < 0)
return PA_HOOK_CANCEL;
return PA_HOOK_OK;
}
static void reserve_done(struct userdata *u) {
pa_assert(u);
if (u->reserve_slot) {
pa_hook_slot_free(u->reserve_slot);
u->reserve_slot = NULL;
}
if (u->reserve) {
pa_reserve_wrapper_unref(u->reserve);
u->reserve = NULL;
}
}
static int reserve_init(struct userdata *u, const char *dname) {
char *rname;
pa_assert(u);
pa_assert(dname);
if (u->reserve)
return 0;
/* We are resuming, try to lock the device */
if (!(rname = pa_alsa_get_reserve_name(dname)))
return 0;
u->reserve = pa_reserve_wrapper_get(u->core, rname);
pa_xfree(rname);
if (!(u->reserve))
return -1;
pa_assert(!u->reserve_slot);
u->reserve_slot = pa_hook_connect(pa_reserve_wrapper_hook(u->reserve), PA_HOOK_NORMAL, (pa_hook_cb_t) reserve_cb, u);
return 0;
}
static void fix_min_sleep_wakeup(struct userdata *u) {
size_t max_use, max_use_2;
pa_assert(u);
@ -765,6 +819,25 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
return pa_source_process_msg(o, code, data, offset, chunk);
}
/* Called from main context */
static int source_set_state_cb(pa_source *s, pa_source_state_t new_state) {
pa_source_state_t old_state;
struct userdata *u;
pa_source_assert_ref(s);
pa_assert_se(u = s->userdata);
old_state = pa_source_get_state(u->source);
if (PA_SINK_IS_OPENED(old_state) && new_state == PA_SINK_SUSPENDED)
reserve_done(u);
else if (old_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(new_state))
if (reserve_init(u, u->device_name) < 0)
return -1;
return 0;
}
static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
struct userdata *u = snd_mixer_elem_get_callback_private(elem);
@ -1316,6 +1389,11 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC*2, DEFAULT_TSCHED_WATERMARK_USEC*2, TRUE, 5);
pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
if (reserve_init(u, pa_modargs_get_value(
ma, "device_id",
pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0)
goto fail;
b = use_mmap;
d = use_tsched;
@ -1414,6 +1492,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->userdata = u;
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
@ -1519,6 +1598,8 @@ static void userdata_free(struct userdata *u) {
if (u->smoother)
pa_smoother_free(u->smoother);
reserve_done(u);
pa_xfree(u->device_name);
pa_xfree(u);
}

View file

@ -1708,3 +1708,24 @@ char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm) {
return pa_alsa_get_driver_name(card);
}
char *pa_alsa_get_reserve_name(const char *device) {
const char *t;
int i;
pa_assert(device);
if ((t = strchr(device, ':')))
device = t+1;
if ((i = snd_card_get_index(device)) < 0) {
int32_t k;
if (pa_atoi(device, &k) < 0)
return NULL;
i = (int) k;
}
return pa_sprintf_malloc("Audio%i", i);
}

View file

@ -136,4 +136,6 @@ 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);
#endif

View file

@ -30,6 +30,8 @@
#include <pulsecore/modargs.h>
#include <pulsecore/queue.h>
#include <modules/reserve-wrap.h>
#include "alsa-util.h"
#include "alsa-sink.h"
#include "alsa-source.h"
@ -273,11 +275,13 @@ static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *de
pa_xfree(t);
}
int pa__init(pa_module*m) {
int pa__init(pa_module *m) {
pa_card_new_data data;
pa_modargs *ma;
int alsa_card_index;
struct userdata *u;
char rname[32];
pa_reserve_wrapper *reserve = NULL;
pa_alsa_redirect_errors_inc();
snd_config_update_free_global();
@ -303,6 +307,11 @@ int pa__init(pa_module*m) {
goto fail;
}
pa_snprintf(rname, sizeof(rname), "Audio%i", alsa_card_index);
if (!(reserve = pa_reserve_wrapper_get(m->core, rname)))
goto fail;
pa_card_new_data_init(&data);
data.driver = __FILE__;
data.module = m;
@ -335,11 +344,16 @@ int pa__init(pa_module*m) {
init_profile(u);
pa_reserve_wrapper_unref(reserve);
return 0;
fail:
if (reserve)
pa_reserve_wrapper_unref(reserve);
pa__done(m);
return -1;
}

158
src/modules/reserve-wrap.c Normal file
View file

@ -0,0 +1,158 @@
/***
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 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 <config.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/i18n.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/shared.h>
#include <modules/dbus-util.h>
#include "reserve.h"
#include "reserve-wrap.h"
struct pa_reserve_wrapper {
PA_REFCNT_DECLARE;
pa_core *core;
pa_dbus_connection *connection;
pa_hook hook;
struct rd_device *device;
char *shared_name;
};
static void reserve_wrapper_free(pa_reserve_wrapper *r) {
pa_assert(r);
if (r->device)
rd_release(r->device);
pa_hook_done(&r->hook);
if (r->connection)
pa_dbus_connection_unref(r->connection);
if (r->shared_name) {
pa_assert_se(pa_shared_remove(r->core, r->shared_name) >= 0);
pa_xfree(r->shared_name);
}
pa_xfree(r);
}
static int request_cb(rd_device *d, int forced) {
pa_reserve_wrapper *r;
int k;
pa_assert(d);
pa_assert_se(r = rd_get_userdata(d));
pa_assert(PA_REFCNT_VALUE(r) >= 1);
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_reserve_wrapper_unref(r);
return k < 0 ? -1 : 1;
}
pa_reserve_wrapper* pa_reserve_wrapper_get(pa_core *c, const char *device_name) {
pa_reserve_wrapper *r;
DBusError error;
int k;
char *t;
dbus_error_init(&error);
pa_assert(c);
pa_assert(device_name);
t = pa_sprintf_malloc("reserve-wrapper@%s", device_name);
if ((r = pa_shared_get(c, t))) {
pa_xfree(t);
pa_assert(PA_REFCNT_VALUE(r) >= 1);
PA_REFCNT_INC(r);
return r;
}
r = pa_xnew0(pa_reserve_wrapper, 1);
PA_REFCNT_INIT(r);
r->core = c;
pa_hook_init(&r->hook, r);
r->shared_name = t;
pa_assert_se(pa_shared_set(c, r->shared_name, r) >= 0);
if (!(r->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
pa_log_error("Unable to contact D-Bus session bus: %s: %s", error.name, error.message);
goto fail;
}
if ((k = rd_acquire(
&r->device,
pa_dbus_connection_get(r->connection),
device_name,
_("PulseAudio Sound Server"),
0,
request_cb,
&error)) < 0) {
pa_log_error("Failed to acquire reservation lock on device '%s': %s", device_name, pa_cstrerror(-k));
goto fail;
}
pa_log_debug("Successfully acquired reservation lock on device '%s'", device_name);
rd_set_userdata(r->device, r);
return r;
fail:
reserve_wrapper_free(r);
return NULL;
}
void pa_reserve_wrapper_unref(pa_reserve_wrapper *r) {
pa_assert(r);
pa_assert(PA_REFCNT_VALUE(r) >= 1);
if (PA_REFCNT_DEC(r) > 0)
return;
reserve_wrapper_free(r);
}
pa_hook* pa_reserve_wrapper_hook(pa_reserve_wrapper *r) {
pa_assert(r);
pa_assert(PA_REFCNT_VALUE(r) >= 1);
return &r->hook;
}

View file

@ -0,0 +1,36 @@
#ifndef fooreservewraphfoo
#define fooreservewraphfoo
/***
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 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 <pulsecore/core.h>
#include <pulsecore/hook-list.h>
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);
#endif

624
src/modules/reserve.c Normal file
View file

@ -0,0 +1,624 @@
/***
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 <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "reserve.h"
struct rd_device {
int ref;
char *device_name;
char *application_name;
char *application_device_name;
char *service_name;
char *object_path;
int32_t priority;
DBusConnection *connection;
int owning:1;
int registered:1;
int filtering:1;
int gave_up:1;
rd_request_cb_t request_cb;
void *userdata;
};
#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
#define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/"
static const char introspection[] =
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
"<node>"
" <interface name=\"org.freedesktop.ReserveDevice1\">"
" <method name=\"RequestRelease\">"
" <arg name=\"priority\" type=\"i\" direction=\"in\"/>"
" <arg name=\"result\" type=\"b\" direction=\"out\"/>"
" </method>"
" <property name=\"Priority\" type=\"i\" access=\"read\"/>"
" <property name=\"ApplicationName\" type=\"s\" access=\"read\"/>"
" <property name=\"ApplicationDeviceName\" type=\"s\" access=\"read\"/>"
" </interface>"
" <interface name=\"org.freedesktop.DBus.Properties\">"
" <method name=\"Get\">"
" <arg name=\"interface\" direction=\"in\" type=\"s\"/>"
" <arg name=\"property\" direction=\"in\" type=\"s\"/>"
" <arg name=\"value\" direction=\"out\" type=\"v\"/>"
" </method>"
" </interface>"
" <interface name=\"org.freedesktop.DBus.Introspectable\">"
" <method name=\"Introspect\">"
" <arg name=\"data\" type=\"s\" direction=\"out\"/>"
" </method>"
" </interface>"
"</node>";
static dbus_bool_t add_variant(
DBusMessage *m,
int type,
const void *data) {
DBusMessageIter iter, sub;
char t[2];
t[0] = (char) type;
t[1] = 0;
dbus_message_iter_init_append(m, &iter);
if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, t, &sub))
return FALSE;
if (!dbus_message_iter_append_basic(&sub, type, data))
return FALSE;
if (!dbus_message_iter_close_container(&iter, &sub))
return FALSE;
return TRUE;
}
static DBusHandlerResult object_handler(
DBusConnection *c,
DBusMessage *m,
void *userdata) {
rd_device *d;
DBusError error;
DBusMessage *reply = NULL;
dbus_error_init(&error);
d = userdata;
assert(d->ref >= 1);
if (dbus_message_is_method_call(
m,
"org.freedesktop.ReserveDevice1",
"RequestRelease")) {
int32_t priority;
dbus_bool_t ret;
if (!dbus_message_get_args(
m,
&error,
DBUS_TYPE_INT32, &priority,
DBUS_TYPE_INVALID))
goto invalid;
ret = FALSE;
if (priority > d->priority && d->request_cb) {
d->ref++;
if (d->request_cb(d, 0) > 0) {
ret = TRUE;
d->gave_up = 1;
}
rd_release(d);
}
if (!(reply = dbus_message_new_method_return(m)))
goto oom;
if (!dbus_message_append_args(
reply,
DBUS_TYPE_BOOLEAN, &ret,
DBUS_TYPE_INVALID))
goto oom;
if (!dbus_connection_send(c, reply, NULL))
goto oom;
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_method_call(
m,
"org.freedesktop.DBus.Properties",
"Get")) {
const char *interface, *property;
if (!dbus_message_get_args(
m,
&error,
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID))
goto invalid;
if (strcmp(interface, "org.freedesktop.ReserveDevice1") == 0) {
const char *empty = "";
if (strcmp(property, "ApplicationName") == 0 && d->application_name) {
if (!(reply = dbus_message_new_method_return(m)))
goto oom;
if (!add_variant(
reply,
DBUS_TYPE_STRING,
d->application_name ? (const char**) &d->application_name : &empty))
goto oom;
} else if (strcmp(property, "ApplicationDeviceName") == 0) {
if (!(reply = dbus_message_new_method_return(m)))
goto oom;
if (!add_variant(
reply,
DBUS_TYPE_STRING,
d->application_device_name ? (const char**) &d->application_device_name : &empty))
goto oom;
} else if (strcmp(property, "Priority") == 0) {
if (!(reply = dbus_message_new_method_return(m)))
goto oom;
if (!add_variant(
reply,
DBUS_TYPE_INT32,
&d->priority))
goto oom;
} else {
if (!(reply = dbus_message_new_error_printf(
m,
DBUS_ERROR_UNKNOWN_METHOD,
"Unknown property %s",
property)))
goto oom;
}
if (!dbus_connection_send(c, reply, NULL))
goto oom;
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
} else if (dbus_message_is_method_call(
m,
"org.freedesktop.DBus.Introspectable",
"Introspect")) {
const char *i = introspection;
if (!(reply = dbus_message_new_method_return(m)))
goto oom;
if (!dbus_message_append_args(
reply,
DBUS_TYPE_STRING,
&i,
DBUS_TYPE_INVALID))
goto oom;
if (!dbus_connection_send(c, reply, NULL))
goto oom;
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
invalid:
if (reply)
dbus_message_unref(reply);
if (!(reply = dbus_message_new_error(
m,
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;
}
static DBusHandlerResult filter_handler(
DBusConnection *c,
DBusMessage *m,
void *userdata) {
DBusMessage *reply;
rd_device *d;
DBusError error;
dbus_error_init(&error);
d = userdata;
if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameLost")) {
const char *name;
if (!dbus_message_get_args(
m,
&error,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID))
goto invalid;
if (strcmp(name, d->service_name) == 0 && d->owning) {
d->owning = 0;
if (!d->gave_up) {
d->ref++;
if (d->request_cb)
d->request_cb(d, 1);
d->gave_up = 1;
rd_release(d);
}
return DBUS_HANDLER_RESULT_HANDLED;
}
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
invalid:
if (!(reply = dbus_message_new_error(
m,
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;
}
static const struct DBusObjectPathVTable vtable ={
.message_function = object_handler
};
int rd_acquire(
rd_device **_d,
DBusConnection *connection,
const char *device_name,
const char *application_name,
int32_t priority,
rd_request_cb_t request_cb,
DBusError *error) {
rd_device *d = NULL;
int r, k;
DBusError _error;
DBusMessage *m = NULL, *reply = NULL;
dbus_bool_t good;
if (!error)
error = &_error;
dbus_error_init(error);
if (!_d)
return -EINVAL;
if (!connection)
return -EINVAL;
if (!device_name)
return -EINVAL;
if (!request_cb && priority != INT32_MAX)
return -EINVAL;
if (!(d = calloc(sizeof(rd_device), 1)))
return -ENOMEM;
d->ref = 1;
if (!(d->device_name = strdup(device_name))) {
r = -ENOMEM;
goto fail;
}
if (!(d->application_name = strdup(application_name))) {
r = -ENOMEM;
goto fail;
}
d->priority = priority;
d->connection = dbus_connection_ref(connection);
d->request_cb = request_cb;
if (!(d->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) {
r = -ENOMEM;
goto fail;
}
sprintf(d->service_name, SERVICE_PREFIX "%s", d->device_name);
if (!(d->object_path = malloc(sizeof(OBJECT_PREFIX) + strlen(device_name)))) {
r = -ENOMEM;
goto fail;
}
sprintf(d->object_path, OBJECT_PREFIX "%s", d->device_name);
if ((k = dbus_bus_request_name(
d->connection,
d->service_name,
DBUS_NAME_FLAG_DO_NOT_QUEUE|
(priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0),
error)) < 0) {
r = -EIO;
goto fail;
}
if (k == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
goto success;
if (k != DBUS_REQUEST_NAME_REPLY_EXISTS) {
r = -EIO;
goto fail;
}
if (priority <= INT32_MIN) {
r = -EBUSY;
goto fail;
}
if (!(m = dbus_message_new_method_call(
d->service_name,
d->object_path,
"org.freedesktop.ReserveDevice1",
"RequestRelease"))) {
r = -ENOMEM;
goto fail;
}
if (!dbus_message_append_args(
m,
DBUS_TYPE_INT32, &d->priority,
DBUS_TYPE_INVALID)) {
r = -ENOMEM;
goto fail;
}
if (!(reply = dbus_connection_send_with_reply_and_block(
d->connection,
m,
-1,
error))) {
r = -EIO;
goto fail;
}
if (!dbus_message_get_args(
reply,
error,
DBUS_TYPE_BOOLEAN, &good,
DBUS_TYPE_INVALID)) {
r = -EIO;
goto fail;
}
if (!good) {
r = -EBUSY;
goto fail;
}
if ((k = dbus_bus_request_name(
d->connection,
d->service_name,
DBUS_NAME_FLAG_DO_NOT_QUEUE|
(priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0)|
DBUS_NAME_FLAG_REPLACE_EXISTING,
error)) < 0) {
r = -EIO;
goto fail;
}
if (k != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
r = -EIO;
goto fail;
}
success:
d->owning = 1;
if (!(dbus_connection_register_object_path(
d->connection,
d->object_path,
&vtable,
d))) {
r = -ENOMEM;
goto fail;
}
d->registered = 1;
if (!dbus_connection_add_filter(
d->connection,
filter_handler,
d,
NULL)) {
r = -ENOMEM;
goto fail;
}
d->filtering = 1;
*_d = d;
return 0;
fail:
if (m)
dbus_message_unref(m);
if (reply)
dbus_message_unref(reply);
if (&_error == error)
dbus_error_free(&_error);
if (d)
rd_release(d);
return r;
}
void rd_release(
rd_device *d) {
if (!d)
return;
assert(d->ref > 0);
if (--d->ref)
return;
if (d->filtering)
dbus_connection_remove_filter(
d->connection,
filter_handler,
d);
if (d->registered)
dbus_connection_unregister_object_path(
d->connection,
d->object_path);
if (d->owning) {
DBusError error;
dbus_error_init(&error);
dbus_bus_release_name(
d->connection,
d->service_name,
&error);
dbus_error_free(&error);
}
free(d->device_name);
free(d->application_name);
free(d->application_device_name);
free(d->service_name);
free(d->object_path);
if (d->connection)
dbus_connection_unref(d->connection);
free(d);
}
int rd_set_application_device_name(rd_device *d, const char *n) {
char *t;
if (!d)
return -EINVAL;
assert(d->ref > 0);
if (!(t = strdup(n)))
return -ENOMEM;
free(d->application_device_name);
d->application_device_name = t;
return 0;
}
void rd_set_userdata(rd_device *d, void *userdata) {
if (!d)
return;
assert(d->ref > 0);
d->userdata = userdata;
}
void* rd_get_userdata(rd_device *d) {
if (!d)
return NULL;
assert(d->ref > 0);
return d->userdata;
}

68
src/modules/reserve.h Normal file
View file

@ -0,0 +1,68 @@
#ifndef fooreservehfoo
#define fooreservehfoo
/***
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 <dbus/dbus.h>
#include <inttypes.h>
typedef struct rd_device rd_device;
/* Prototype for a function that is called whenever someone else wants
* your app to release the device you having locked. A return value <=
* 0 denies the request, a positive return value agrees to it. Before
* returning your application should close the device in question
* completely to make sure the new application may acceess it. */
typedef int (*rd_request_cb_t)(
rd_device *d,
int forced); /* Non-zero if an application forcibly took the lock away without asking. If this is the case then the return value of this call is ignored. */
/* Try to lock the 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 rd_acquire(
rd_device **d, /* On success a pointer to the newly allocated rd_device object will be filled in here */
DBusConnection *connection,
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 */
rd_request_cb_t request_cb, /* Will be called whenever someone asks that this device shall be released. May be NULL if priority is INT32_MAX */
DBusError *error); /* If we fail due to a D-Bus related issue the error will be filled in here. May be NULL. */
/* Unlock (if needed) and destroy a rd_device object again */
void rd_release(rd_device *d);
/* Set the application device name for a rd_device object Returns 0 on
* success, a negative errno style return value on error. */
int rd_set_application_device_name(rd_device *d, const char *name);
/* Attach a userdata pointer to a rd_device */
void rd_set_userdata(rd_device *d, void *userdata);
/* Query the userdata pointer from a rd_device. Returns NULL if no
* userdata was set. */
void* rd_get_userdata(rd_device *d);
#endif