mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	implement device reservation scheme
This commit is contained in:
		
							parent
							
								
									3c73025bf5
								
							
						
					
					
						commit
						c341010304
					
				
					 10 changed files with 1102 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -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 {" ; \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
									
								
							
							
						
						
									
										158
									
								
								src/modules/reserve-wrap.c
									
										
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								src/modules/reserve-wrap.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/modules/reserve-wrap.h
									
										
									
									
									
										Normal 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
									
								
							
							
						
						
									
										624
									
								
								src/modules/reserve.c
									
										
									
									
									
										Normal 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
									
								
							
							
						
						
									
										68
									
								
								src/modules/reserve.h
									
										
									
									
									
										Normal 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
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue