Blame pulse/ctl_pulse.c

Packit Service cd2a00
/*-*- linux-c -*-*/
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * ALSA <-> PulseAudio mixer control plugin
Packit Service cd2a00
 *
Packit Service cd2a00
 * Copyright (c) 2006 by Pierre Ossman <ossman@cendio.se>
Packit Service cd2a00
 *
Packit Service cd2a00
 * This library is free software; you can redistribute it and/or modify
Packit Service cd2a00
 * it under the terms of the GNU Lesser General Public License as
Packit Service cd2a00
 * published by the Free Software Foundation; either version 2.1 of
Packit Service cd2a00
 * the License, or (at your option) any later version.
Packit Service cd2a00
 *
Packit Service cd2a00
 * This program is distributed in the hope that it will be useful,
Packit Service cd2a00
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service cd2a00
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service cd2a00
 * GNU Lesser General Public License for more details.
Packit Service cd2a00
 *
Packit Service cd2a00
 * You should have received a copy of the GNU Lesser General Public
Packit Service cd2a00
 * License along with this library; if not, write to the Free Software
Packit Service cd2a00
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service cd2a00
 */
Packit Service cd2a00
Packit Service cd2a00
#include <sys/poll.h>
Packit Service cd2a00
Packit Service cd2a00
#include <alsa/asoundlib.h>
Packit Service cd2a00
#include <alsa/control_external.h>
Packit Service cd2a00
Packit Service cd2a00
#include "pulse.h"
Packit Service cd2a00
Packit Service cd2a00
typedef struct snd_ctl_pulse {
Packit Service cd2a00
	snd_ctl_ext_t ext;
Packit Service cd2a00
Packit Service cd2a00
	snd_pulse_t *p;
Packit Service cd2a00
Packit Service cd2a00
	char *source;
Packit Service cd2a00
	char *sink;
Packit Service cd2a00
Packit Service cd2a00
	pa_cvolume sink_volume;
Packit Service cd2a00
	pa_cvolume source_volume;
Packit Service cd2a00
Packit Service cd2a00
	int sink_muted;
Packit Service cd2a00
	int source_muted;
Packit Service cd2a00
Packit Service cd2a00
	int subscribed;
Packit Service cd2a00
	int updated;
Packit Service cd2a00
} snd_ctl_pulse_t;
Packit Service cd2a00
Packit Service cd2a00
#define SOURCE_VOL_NAME "Capture Volume"
Packit Service cd2a00
#define SOURCE_MUTE_NAME "Capture Switch"
Packit Service cd2a00
#define SINK_VOL_NAME "Master Playback Volume"
Packit Service cd2a00
#define SINK_MUTE_NAME "Master Playback Switch"
Packit Service cd2a00
Packit Service cd2a00
#define UPDATE_SINK_VOL     0x01
Packit Service cd2a00
#define UPDATE_SINK_MUTE    0x02
Packit Service cd2a00
#define UPDATE_SOURCE_VOL   0x04
Packit Service cd2a00
#define UPDATE_SOURCE_MUTE  0x08
Packit Service cd2a00
Packit Service cd2a00
static void sink_info_cb(pa_context * c, const pa_sink_info * i,
Packit Service cd2a00
			 int is_last, void *userdata)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = (snd_ctl_pulse_t *) userdata;
Packit Service cd2a00
	int changed = 0;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (is_last) {
Packit Service cd2a00
		pa_threaded_mainloop_signal(ctl->p->mainloop, 0);
Packit Service cd2a00
		return;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	assert(i);
Packit Service cd2a00
Packit Service cd2a00
	if (!!ctl->sink_muted != !!i->mute) {
Packit Service cd2a00
		ctl->sink_muted = i->mute;
Packit Service cd2a00
		ctl->updated |= UPDATE_SINK_MUTE;
Packit Service cd2a00
		changed = 1;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (!pa_cvolume_equal(&ctl->sink_volume, &i->volume)) {
Packit Service cd2a00
		ctl->sink_volume = i->volume;
Packit Service cd2a00
		ctl->updated |= UPDATE_SINK_VOL;
Packit Service cd2a00
		changed = 1;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (changed)
Packit Service cd2a00
		pulse_poll_activate(ctl->p);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static void source_info_cb(pa_context * c, const pa_source_info * i,
Packit Service cd2a00
			   int is_last, void *userdata)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = (snd_ctl_pulse_t *) userdata;
Packit Service cd2a00
	int changed = 0;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (is_last) {
Packit Service cd2a00
		pa_threaded_mainloop_signal(ctl->p->mainloop, 0);
Packit Service cd2a00
		return;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	assert(i);
Packit Service cd2a00
Packit Service cd2a00
	if (!!ctl->source_muted != !!i->mute) {
Packit Service cd2a00
		ctl->source_muted = i->mute;
Packit Service cd2a00
		ctl->updated |= UPDATE_SOURCE_MUTE;
Packit Service cd2a00
		changed = 1;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (!pa_cvolume_equal(&ctl->source_volume, &i->volume)) {
Packit Service cd2a00
		ctl->source_volume = i->volume;
Packit Service cd2a00
		ctl->updated |= UPDATE_SOURCE_VOL;
Packit Service cd2a00
		changed = 1;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (changed)
Packit Service cd2a00
		pulse_poll_activate(ctl->p);
Packit Service cd2a00
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static void event_cb(pa_context * c, pa_subscription_event_type_t t,
Packit Service cd2a00
		     uint32_t index, void *userdata)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = (snd_ctl_pulse_t *) userdata;
Packit Service cd2a00
	pa_operation *o;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->p || !ctl->p->mainloop || !ctl->p->context)
Packit Service cd2a00
		return;
Packit Service cd2a00
Packit Service cd2a00
	o = pa_context_get_sink_info_by_name(ctl->p->context, ctl->sink,
Packit Service cd2a00
					     sink_info_cb, ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (o)
Packit Service cd2a00
		pa_operation_unref(o);
Packit Service cd2a00
Packit Service cd2a00
	o = pa_context_get_source_info_by_name(ctl->p->context,
Packit Service cd2a00
					       ctl->source, source_info_cb,
Packit Service cd2a00
					       ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (o)
Packit Service cd2a00
		pa_operation_unref(o);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int pulse_update_volume(snd_ctl_pulse_t * ctl)
Packit Service cd2a00
{
Packit Service cd2a00
	int err;
Packit Service cd2a00
	pa_operation *o;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->p)
Packit Service cd2a00
		return -EBADFD;
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_check_connection(ctl->p);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		return err;
Packit Service cd2a00
Packit Service cd2a00
	o = pa_context_get_sink_info_by_name(ctl->p->context, ctl->sink,
Packit Service cd2a00
					     sink_info_cb, ctl);
Packit Service cd2a00
	if (o) {
Packit Service cd2a00
		err = pulse_wait_operation(ctl->p, o);
Packit Service cd2a00
		pa_operation_unref(o);
Packit Service cd2a00
	} else
Packit Service cd2a00
		err = -EIO;
Packit Service cd2a00
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		return err;
Packit Service cd2a00
Packit Service cd2a00
	o = pa_context_get_source_info_by_name(ctl->p->context,
Packit Service cd2a00
					       ctl->source, source_info_cb,
Packit Service cd2a00
					       ctl);
Packit Service cd2a00
	if (o) {
Packit Service cd2a00
		err = pulse_wait_operation(ctl->p, o);
Packit Service cd2a00
		pa_operation_unref(o);
Packit Service cd2a00
	} else
Packit Service cd2a00
		err = -EIO;
Packit Service cd2a00
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		return err;
Packit Service cd2a00
Packit Service cd2a00
	return 0;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int pulse_elem_count(snd_ctl_ext_t * ext)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = ext->private_data;
Packit Service cd2a00
	int count = 0, err;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->p || !ctl->p->mainloop)
Packit Service cd2a00
		return -EBADFD;
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_lock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_check_connection(ctl->p);
Packit Service cd2a00
	if (err < 0) {
Packit Service cd2a00
		count = err;
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (ctl->source)
Packit Service cd2a00
		count += 2;
Packit Service cd2a00
	if (ctl->sink)
Packit Service cd2a00
		count += 2;
Packit Service cd2a00
Packit Service cd2a00
finish:
Packit Service cd2a00
	pa_threaded_mainloop_unlock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	return count;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int pulse_elem_list(snd_ctl_ext_t * ext, unsigned int offset,
Packit Service cd2a00
			   snd_ctl_elem_id_t * id)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = ext->private_data;
Packit Service cd2a00
	int err;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->p || !ctl->p->mainloop)
Packit Service cd2a00
		return -EBADFD;
Packit Service cd2a00
Packit Service cd2a00
	snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_lock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_check_connection(ctl->p);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
Packit Service cd2a00
	if (ctl->source) {
Packit Service cd2a00
		if (offset == 0)
Packit Service cd2a00
			snd_ctl_elem_id_set_name(id, SOURCE_VOL_NAME);
Packit Service cd2a00
		else if (offset == 1)
Packit Service cd2a00
			snd_ctl_elem_id_set_name(id, SOURCE_MUTE_NAME);
Packit Service cd2a00
	} else
Packit Service cd2a00
		offset += 2;
Packit Service cd2a00
Packit Service cd2a00
	err = 0;
Packit Service cd2a00
Packit Service cd2a00
finish:
Packit Service cd2a00
	pa_threaded_mainloop_unlock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	if (err >= 0) {
Packit Service cd2a00
		if (offset == 2)
Packit Service cd2a00
			snd_ctl_elem_id_set_name(id, SINK_VOL_NAME);
Packit Service cd2a00
		else if (offset == 3)
Packit Service cd2a00
			snd_ctl_elem_id_set_name(id, SINK_MUTE_NAME);
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	return err;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static snd_ctl_ext_key_t pulse_find_elem(snd_ctl_ext_t * ext,
Packit Service cd2a00
					 const snd_ctl_elem_id_t * id)
Packit Service cd2a00
{
Packit Service cd2a00
	const char *name;
Packit Service cd2a00
	unsigned int numid;
Packit Service cd2a00
Packit Service cd2a00
	numid = snd_ctl_elem_id_get_numid(id);
Packit Service cd2a00
	if (numid > 0 && numid <= 4)
Packit Service cd2a00
		return numid - 1;
Packit Service cd2a00
Packit Service cd2a00
	name = snd_ctl_elem_id_get_name(id);
Packit Service cd2a00
Packit Service cd2a00
	if (strcmp(name, SOURCE_VOL_NAME) == 0)
Packit Service cd2a00
		return 0;
Packit Service cd2a00
	if (strcmp(name, SOURCE_MUTE_NAME) == 0)
Packit Service cd2a00
		return 1;
Packit Service cd2a00
	if (strcmp(name, SINK_VOL_NAME) == 0)
Packit Service cd2a00
		return 2;
Packit Service cd2a00
	if (strcmp(name, SINK_MUTE_NAME) == 0)
Packit Service cd2a00
		return 3;
Packit Service cd2a00
Packit Service cd2a00
	return SND_CTL_EXT_KEY_NOT_FOUND;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int pulse_get_attribute(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
Packit Service cd2a00
			       int *type, unsigned int *acc,
Packit Service cd2a00
			       unsigned int *count)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = ext->private_data;
Packit Service cd2a00
	int err = 0;
Packit Service cd2a00
Packit Service cd2a00
	if (key > 3)
Packit Service cd2a00
		return -EINVAL;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->p || !ctl->p->mainloop)
Packit Service cd2a00
		return -EBADFD;
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_lock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_check_connection(ctl->p);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_update_volume(ctl);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
Packit Service cd2a00
	if (key & 1)
Packit Service cd2a00
		*type = SND_CTL_ELEM_TYPE_BOOLEAN;
Packit Service cd2a00
	else
Packit Service cd2a00
		*type = SND_CTL_ELEM_TYPE_INTEGER;
Packit Service cd2a00
Packit Service cd2a00
	*acc = SND_CTL_EXT_ACCESS_READWRITE;
Packit Service cd2a00
Packit Service cd2a00
	if (key == 0)
Packit Service cd2a00
		*count = ctl->source_volume.channels;
Packit Service cd2a00
	else if (key == 2)
Packit Service cd2a00
		*count = ctl->sink_volume.channels;
Packit Service cd2a00
	else
Packit Service cd2a00
		*count = 1;
Packit Service cd2a00
Packit Service cd2a00
      finish:
Packit Service cd2a00
	pa_threaded_mainloop_unlock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	return err;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int pulse_get_integer_info(snd_ctl_ext_t * ext,
Packit Service cd2a00
				  snd_ctl_ext_key_t key, long *imin,
Packit Service cd2a00
				  long *imax, long *istep)
Packit Service cd2a00
{
Packit Service cd2a00
	*istep = 1;
Packit Service cd2a00
	*imin = 0;
Packit Service cd2a00
	*imax = PA_VOLUME_NORM;
Packit Service cd2a00
Packit Service cd2a00
	return 0;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int pulse_read_integer(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
Packit Service cd2a00
			      long *value)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = ext->private_data;
Packit Service cd2a00
	int err = 0, i;
Packit Service cd2a00
	pa_cvolume *vol = NULL;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->p || !ctl->p->mainloop)
Packit Service cd2a00
		return -EBADFD;
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_lock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_check_connection(ctl->p);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_update_volume(ctl);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
Packit Service cd2a00
	switch (key) {
Packit Service cd2a00
	case 0:
Packit Service cd2a00
		vol = &ctl->source_volume;
Packit Service cd2a00
		break;
Packit Service cd2a00
	case 1:
Packit Service cd2a00
		*value = !ctl->source_muted;
Packit Service cd2a00
		break;
Packit Service cd2a00
	case 2:
Packit Service cd2a00
		vol = &ctl->sink_volume;
Packit Service cd2a00
		break;
Packit Service cd2a00
	case 3:
Packit Service cd2a00
		*value = !ctl->sink_muted;
Packit Service cd2a00
		break;
Packit Service cd2a00
	default:
Packit Service cd2a00
		err = -EINVAL;
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (vol) {
Packit Service cd2a00
		for (i = 0; i < vol->channels; i++)
Packit Service cd2a00
			value[i] = vol->values[i];
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
      finish:
Packit Service cd2a00
	pa_threaded_mainloop_unlock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	return err;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int pulse_write_integer(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
Packit Service cd2a00
			       long *value)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = ext->private_data;
Packit Service cd2a00
	int err = 0, i;
Packit Service cd2a00
	pa_operation *o;
Packit Service cd2a00
	pa_cvolume *vol = NULL;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->p || !ctl->p->mainloop)
Packit Service cd2a00
		return -EBADFD;
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_lock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_check_connection(ctl->p);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_update_volume(ctl);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
Packit Service cd2a00
	switch (key) {
Packit Service cd2a00
	case 0:
Packit Service cd2a00
		vol = &ctl->source_volume;
Packit Service cd2a00
		break;
Packit Service cd2a00
	case 1:
Packit Service cd2a00
		if (!!ctl->source_muted == !*value)
Packit Service cd2a00
			goto finish;
Packit Service cd2a00
		ctl->source_muted = !*value;
Packit Service cd2a00
		break;
Packit Service cd2a00
	case 2:
Packit Service cd2a00
		vol = &ctl->sink_volume;
Packit Service cd2a00
		break;
Packit Service cd2a00
	case 3:
Packit Service cd2a00
		if (!!ctl->sink_muted == !*value)
Packit Service cd2a00
			goto finish;
Packit Service cd2a00
		ctl->sink_muted = !*value;
Packit Service cd2a00
		break;
Packit Service cd2a00
	default:
Packit Service cd2a00
		err = -EINVAL;
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (vol) {
Packit Service cd2a00
		for (i = 0; i < vol->channels; i++)
Packit Service cd2a00
			if (value[i] != vol->values[i])
Packit Service cd2a00
				break;
Packit Service cd2a00
Packit Service cd2a00
		if (i == vol->channels)
Packit Service cd2a00
			goto finish;
Packit Service cd2a00
Packit Service cd2a00
		for (i = 0; i < vol->channels; i++)
Packit Service cd2a00
			vol->values[i] = value[i];
Packit Service cd2a00
Packit Service cd2a00
		if (key == 0)
Packit Service cd2a00
			o = pa_context_set_source_volume_by_name(ctl->p->
Packit Service cd2a00
								 context,
Packit Service cd2a00
								 ctl->
Packit Service cd2a00
								 source,
Packit Service cd2a00
								 vol,
Packit Service cd2a00
								 pulse_context_success_cb,
Packit Service cd2a00
								 ctl->p);
Packit Service cd2a00
		else
Packit Service cd2a00
			o = pa_context_set_sink_volume_by_name(ctl->p->
Packit Service cd2a00
							       context,
Packit Service cd2a00
							       ctl->sink,
Packit Service cd2a00
							       vol,
Packit Service cd2a00
							       pulse_context_success_cb,
Packit Service cd2a00
							       ctl->p);
Packit Service cd2a00
	} else {
Packit Service cd2a00
		if (key == 1)
Packit Service cd2a00
			o = pa_context_set_source_mute_by_name(ctl->p->
Packit Service cd2a00
							       context,
Packit Service cd2a00
							       ctl->source,
Packit Service cd2a00
							       ctl->
Packit Service cd2a00
							       source_muted,
Packit Service cd2a00
							       pulse_context_success_cb,
Packit Service cd2a00
							       ctl->p);
Packit Service cd2a00
		else
Packit Service cd2a00
			o = pa_context_set_sink_mute_by_name(ctl->p->
Packit Service cd2a00
							     context,
Packit Service cd2a00
							     ctl->sink,
Packit Service cd2a00
							     ctl->
Packit Service cd2a00
							     sink_muted,
Packit Service cd2a00
							     pulse_context_success_cb,
Packit Service cd2a00
							     ctl->p);
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (!o) {
Packit Service cd2a00
		err = -EIO;
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_wait_operation(ctl->p, o);
Packit Service cd2a00
	pa_operation_unref(o);
Packit Service cd2a00
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
Packit Service cd2a00
	err = 1;
Packit Service cd2a00
Packit Service cd2a00
      finish:
Packit Service cd2a00
	pa_threaded_mainloop_unlock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	return err;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static void pulse_subscribe_events(snd_ctl_ext_t * ext, int subscribe)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = ext->private_data;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->p || !ctl->p->mainloop)
Packit Service cd2a00
		return;
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_lock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	ctl->subscribed = !!(subscribe & SND_CTL_EVENT_MASK_VALUE);
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_unlock(ctl->p->mainloop);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int pulse_read_event(snd_ctl_ext_t * ext, snd_ctl_elem_id_t * id,
Packit Service cd2a00
			    unsigned int *event_mask)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = ext->private_data;
Packit Service cd2a00
	int offset;
Packit Service cd2a00
	int err;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->p || !ctl->p->mainloop)
Packit Service cd2a00
		return -EBADFD;
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_lock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_check_connection(ctl->p);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->updated || !ctl->subscribed) {
Packit Service cd2a00
		err = -EAGAIN;
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (ctl->source)
Packit Service cd2a00
		offset = 2;
Packit Service cd2a00
	else
Packit Service cd2a00
		offset = 0;
Packit Service cd2a00
Packit Service cd2a00
	if (ctl->updated & UPDATE_SOURCE_VOL) {
Packit Service cd2a00
		pulse_elem_list(ext, 0, id);
Packit Service cd2a00
		ctl->updated &= ~UPDATE_SOURCE_VOL;
Packit Service cd2a00
	} else if (ctl->updated & UPDATE_SOURCE_MUTE) {
Packit Service cd2a00
		pulse_elem_list(ext, 1, id);
Packit Service cd2a00
		ctl->updated &= ~UPDATE_SOURCE_MUTE;
Packit Service cd2a00
	} else if (ctl->updated & UPDATE_SINK_VOL) {
Packit Service cd2a00
		pulse_elem_list(ext, offset + 0, id);
Packit Service cd2a00
		ctl->updated &= ~UPDATE_SINK_VOL;
Packit Service cd2a00
	} else if (ctl->updated & UPDATE_SINK_MUTE) {
Packit Service cd2a00
		pulse_elem_list(ext, offset + 1, id);
Packit Service cd2a00
		ctl->updated &= ~UPDATE_SINK_MUTE;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	*event_mask = SND_CTL_EVENT_MASK_VALUE;
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->updated)
Packit Service cd2a00
		pulse_poll_deactivate(ctl->p);
Packit Service cd2a00
Packit Service cd2a00
	err = 1;
Packit Service cd2a00
Packit Service cd2a00
      finish:
Packit Service cd2a00
	pa_threaded_mainloop_unlock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	return err;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int pulse_ctl_poll_revents(snd_ctl_ext_t * ext, struct pollfd *pfd,
Packit Service cd2a00
				  unsigned int nfds,
Packit Service cd2a00
				  unsigned short *revents)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = ext->private_data;
Packit Service cd2a00
	int err;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->p || !ctl->p->mainloop)
Packit Service cd2a00
		return -EBADFD;
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_lock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_check_connection(ctl->p);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto finish;
Packit Service cd2a00
Packit Service cd2a00
	if (ctl->updated)
Packit Service cd2a00
		*revents = POLLIN;
Packit Service cd2a00
	else
Packit Service cd2a00
		*revents = 0;
Packit Service cd2a00
Packit Service cd2a00
	err = 0;
Packit Service cd2a00
Packit Service cd2a00
finish:
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_unlock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	return err;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static void pulse_close(snd_ctl_ext_t * ext)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = ext->private_data;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (ctl->p)
Packit Service cd2a00
		pulse_free(ctl->p);
Packit Service cd2a00
Packit Service cd2a00
	free(ctl->source);
Packit Service cd2a00
	free(ctl->sink);
Packit Service cd2a00
	free(ctl);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static const snd_ctl_ext_callback_t pulse_ext_callback = {
Packit Service cd2a00
	.elem_count = pulse_elem_count,
Packit Service cd2a00
	.elem_list = pulse_elem_list,
Packit Service cd2a00
	.find_elem = pulse_find_elem,
Packit Service cd2a00
	.get_attribute = pulse_get_attribute,
Packit Service cd2a00
	.get_integer_info = pulse_get_integer_info,
Packit Service cd2a00
	.read_integer = pulse_read_integer,
Packit Service cd2a00
	.write_integer = pulse_write_integer,
Packit Service cd2a00
	.subscribe_events = pulse_subscribe_events,
Packit Service cd2a00
	.read_event = pulse_read_event,
Packit Service cd2a00
	.poll_revents = pulse_ctl_poll_revents,
Packit Service cd2a00
	.close = pulse_close,
Packit Service cd2a00
};
Packit Service cd2a00
Packit Service cd2a00
static void server_info_cb(pa_context * c, const pa_server_info * i,
Packit Service cd2a00
			   void *userdata)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_ctl_pulse_t *ctl = (snd_ctl_pulse_t *) userdata;
Packit Service cd2a00
Packit Service cd2a00
	assert(ctl && i);
Packit Service cd2a00
Packit Service cd2a00
	if (i->default_source_name && !ctl->source)
Packit Service cd2a00
		ctl->source = strdup(i->default_source_name);
Packit Service cd2a00
	if (i->default_sink_name && !ctl->sink)
Packit Service cd2a00
		ctl->sink = strdup(i->default_sink_name);
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_signal(ctl->p->mainloop, 0);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
SND_CTL_PLUGIN_DEFINE_FUNC(pulse)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_config_iterator_t i, next;
Packit Service cd2a00
	const char *server = NULL;
Packit Service cd2a00
	const char *device = NULL;
Packit Service cd2a00
	const char *source = NULL;
Packit Service cd2a00
	const char *sink = NULL;
Packit Service cd2a00
	const char *fallback_name = NULL;
Packit Service cd2a00
	int err;
Packit Service cd2a00
	snd_ctl_pulse_t *ctl;
Packit Service cd2a00
	pa_operation *o;
Packit Service cd2a00
Packit Service cd2a00
	snd_config_for_each(i, next, conf) {
Packit Service cd2a00
		snd_config_t *n = snd_config_iterator_entry(i);
Packit Service cd2a00
		const char *id;
Packit Service cd2a00
		if (snd_config_get_id(n, &id) < 0)
Packit Service cd2a00
			continue;
Packit Service cd2a00
		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0
Packit Service cd2a00
		    || strcmp(id, "hint") == 0)
Packit Service cd2a00
			continue;
Packit Service cd2a00
		if (strcmp(id, "server") == 0) {
Packit Service cd2a00
			if (snd_config_get_string(n, &server) < 0) {
Packit Service cd2a00
				SNDERR("Invalid type for %s", id);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		if (strcmp(id, "device") == 0) {
Packit Service cd2a00
			if (snd_config_get_string(n, &device) < 0) {
Packit Service cd2a00
				SNDERR("Invalid type for %s", id);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		if (strcmp(id, "source") == 0) {
Packit Service cd2a00
			if (snd_config_get_string(n, &source) < 0) {
Packit Service cd2a00
				SNDERR("Invalid type for %s", id);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		if (strcmp(id, "sink") == 0) {
Packit Service cd2a00
			if (snd_config_get_string(n, &sink) < 0) {
Packit Service cd2a00
				SNDERR("Invalid type for %s", id);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		if (strcmp(id, "fallback") == 0) {
Packit Service cd2a00
			if (snd_config_get_string(n, &fallback_name) < 0) {
Packit Service cd2a00
				SNDERR("Invalid value for %s", id);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		SNDERR("Unknown field %s", id);
Packit Service cd2a00
		return -EINVAL;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (fallback_name && name && !strcmp(name, fallback_name))
Packit Service cd2a00
		fallback_name = NULL; /* no fallback for the same name */
Packit Service cd2a00
Packit Service cd2a00
	ctl = calloc(1, sizeof(*ctl));
Packit Service cd2a00
	if (!ctl)
Packit Service cd2a00
		return -ENOMEM;
Packit Service cd2a00
Packit Service cd2a00
	ctl->p = pulse_new();
Packit Service cd2a00
	if (!ctl->p) {
Packit Service cd2a00
		err = -EIO;
Packit Service cd2a00
		goto error;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	err = pulse_connect(ctl->p, server, fallback_name != NULL);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto error;
Packit Service cd2a00
Packit Service cd2a00
	if (source)
Packit Service cd2a00
		ctl->source = strdup(source);
Packit Service cd2a00
	else if (device)
Packit Service cd2a00
		ctl->source = strdup(device);
Packit Service cd2a00
Packit Service cd2a00
	if ((source || device) && !ctl->source) {
Packit Service cd2a00
		err = -ENOMEM;
Packit Service cd2a00
		goto error;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (sink)
Packit Service cd2a00
		ctl->sink = strdup(sink);
Packit Service cd2a00
	else if (device)
Packit Service cd2a00
		ctl->sink = strdup(device);
Packit Service cd2a00
Packit Service cd2a00
	if ((sink || device) && !ctl->sink) {
Packit Service cd2a00
		err = -ENOMEM;
Packit Service cd2a00
		goto error;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (!ctl->source || !ctl->sink) {
Packit Service cd2a00
		pa_threaded_mainloop_lock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
		o = pa_context_get_server_info(ctl->p->context,
Packit Service cd2a00
					       server_info_cb, ctl);
Packit Service cd2a00
Packit Service cd2a00
		if (o) {
Packit Service cd2a00
			err = pulse_wait_operation(ctl->p, o);
Packit Service cd2a00
			pa_operation_unref(o);
Packit Service cd2a00
		} else
Packit Service cd2a00
			err = -EIO;
Packit Service cd2a00
Packit Service cd2a00
		pa_threaded_mainloop_unlock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
		if (err < 0)
Packit Service cd2a00
			goto error;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_lock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	pa_context_set_subscribe_callback(ctl->p->context, event_cb, ctl);
Packit Service cd2a00
Packit Service cd2a00
	o = pa_context_subscribe(ctl->p->context,
Packit Service cd2a00
				 PA_SUBSCRIPTION_MASK_SINK |
Packit Service cd2a00
				 PA_SUBSCRIPTION_MASK_SOURCE,
Packit Service cd2a00
				 pulse_context_success_cb, ctl->p);
Packit Service cd2a00
Packit Service cd2a00
	if (o) {
Packit Service cd2a00
		err = pulse_wait_operation(ctl->p, o);
Packit Service cd2a00
		pa_operation_unref(o);
Packit Service cd2a00
	} else
Packit Service cd2a00
		err = -EIO;
Packit Service cd2a00
Packit Service cd2a00
	pa_threaded_mainloop_unlock(ctl->p->mainloop);
Packit Service cd2a00
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto error;
Packit Service cd2a00
Packit Service cd2a00
	ctl->ext.version = SND_CTL_EXT_VERSION;
Packit Service cd2a00
	ctl->ext.card_idx = 0;
Packit Service cd2a00
	strncpy(ctl->ext.id, "pulse", sizeof(ctl->ext.id) - 1);
Packit Service cd2a00
	strncpy(ctl->ext.driver, "PulseAudio plugin",
Packit Service cd2a00
		sizeof(ctl->ext.driver) - 1);
Packit Service cd2a00
	strncpy(ctl->ext.name, "PulseAudio", sizeof(ctl->ext.name) - 1);
Packit Service cd2a00
	strncpy(ctl->ext.longname, "PulseAudio",
Packit Service cd2a00
		sizeof(ctl->ext.longname) - 1);
Packit Service cd2a00
	strncpy(ctl->ext.mixername, "PulseAudio",
Packit Service cd2a00
		sizeof(ctl->ext.mixername) - 1);
Packit Service cd2a00
	ctl->ext.poll_fd = ctl->p->main_fd;
Packit Service cd2a00
Packit Service cd2a00
	ctl->ext.callback = &pulse_ext_callback;
Packit Service cd2a00
	ctl->ext.private_data = ctl;
Packit Service cd2a00
Packit Service cd2a00
	err = snd_ctl_ext_create(&ctl->ext, name, mode);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto error;
Packit Service cd2a00
Packit Service cd2a00
	*handlep = ctl->ext.handle;
Packit Service cd2a00
Packit Service cd2a00
	return 0;
Packit Service cd2a00
Packit Service cd2a00
error:
Packit Service cd2a00
	if (ctl->p)
Packit Service cd2a00
		pulse_free(ctl->p);
Packit Service cd2a00
Packit Service cd2a00
	free(ctl->source);
Packit Service cd2a00
	free(ctl->sink);
Packit Service cd2a00
	free(ctl);
Packit Service cd2a00
Packit Service cd2a00
	if (fallback_name)
Packit Service cd2a00
		return snd_ctl_open_fallback(handlep, root,
Packit Service cd2a00
					     fallback_name, name, mode);
Packit Service cd2a00
Packit Service cd2a00
	return err;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
SND_CTL_PLUGIN_SYMBOL(pulse);