Blame pulse/pulse.c

Packit 675970
/*-*- linux-c -*-*/
Packit 675970
Packit 675970
/*
Packit 675970
 * ALSA <-> PulseAudio plugins
Packit 675970
 *
Packit 675970
 * Copyright (c) 2006 by Pierre Ossman <ossman@cendio.se>
Packit 675970
 *
Packit 675970
 * This library is free software; you can redistribute it and/or modify
Packit 675970
 * it under the terms of the GNU Lesser General Public License as
Packit 675970
 * published by the Free Software Foundation; either version 2.1 of
Packit 675970
 * the License, or (at your option) any later version.
Packit 675970
 *
Packit 675970
 * This program is distributed in the hope that it will be useful,
Packit 675970
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 675970
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 675970
 * GNU Lesser General Public License for more details.
Packit 675970
 *
Packit 675970
 * You should have received a copy of the GNU Lesser General Public
Packit 675970
 * License along with this library; if not, write to the Free Software
Packit 675970
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 675970
 */
Packit 675970
Packit 675970
#include <stdio.h>
Packit 675970
#include <unistd.h>
Packit 675970
#include <signal.h>
Packit 675970
#include <sys/poll.h>
Packit 675970
Packit 675970
#include "pulse.h"
Packit 675970
Packit 675970
int pulse_check_connection(snd_pulse_t * p)
Packit 675970
{
Packit 675970
	pa_context_state_t state;
Packit 675970
Packit 675970
	assert(p);
Packit 675970
Packit 675970
	if (!p->context || !p->mainloop)
Packit 675970
		return -EBADFD;
Packit 675970
Packit 675970
	state = pa_context_get_state(p->context);
Packit 675970
Packit 675970
	if (!PA_CONTEXT_IS_GOOD(state))
Packit 675970
		return -EIO;
Packit 675970
Packit 675970
	return 0;
Packit 675970
}
Packit 675970
Packit 675970
void pulse_context_success_cb(pa_context * c, int success, void *userdata)
Packit 675970
{
Packit 675970
	snd_pulse_t *p = userdata;
Packit 675970
Packit 675970
	assert(c);
Packit 675970
	assert(p);
Packit 675970
Packit 675970
	pa_threaded_mainloop_signal(p->mainloop, 0);
Packit 675970
}
Packit 675970
Packit 675970
int pulse_wait_operation(snd_pulse_t * p, pa_operation * o)
Packit 675970
{
Packit 675970
	assert(p);
Packit 675970
	assert(o);
Packit 675970
Packit 675970
	for (;;) {
Packit 675970
		int err;
Packit 675970
Packit 675970
		err = pulse_check_connection(p);
Packit 675970
		if (err < 0)
Packit 675970
			return err;
Packit 675970
Packit 675970
		if (pa_operation_get_state(o) != PA_OPERATION_RUNNING)
Packit 675970
			break;
Packit 675970
Packit 675970
		pa_threaded_mainloop_wait(p->mainloop);
Packit 675970
	}
Packit 675970
Packit 675970
	return 0;
Packit 675970
}
Packit 675970
Packit 675970
static void context_state_cb(pa_context * c, void *userdata)
Packit 675970
{
Packit 675970
	pa_context_state_t state;
Packit 675970
	snd_pulse_t *p = userdata;
Packit 675970
	assert(c);
Packit 675970
Packit 675970
	state = pa_context_get_state(c);
Packit 675970
Packit 675970
	/* When we get disconnected, tell the process */
Packit 675970
	if (!PA_CONTEXT_IS_GOOD(state))
Packit 675970
		pulse_poll_activate(p);
Packit 675970
Packit 675970
	switch (state) {
Packit 675970
	case PA_CONTEXT_READY:
Packit 675970
	case PA_CONTEXT_TERMINATED:
Packit 675970
	case PA_CONTEXT_FAILED:
Packit 675970
		pa_threaded_mainloop_signal(p->mainloop, 0);
Packit 675970
		break;
Packit 675970
Packit 675970
	case PA_CONTEXT_UNCONNECTED:
Packit 675970
	case PA_CONTEXT_CONNECTING:
Packit 675970
	case PA_CONTEXT_AUTHORIZING:
Packit 675970
	case PA_CONTEXT_SETTING_NAME:
Packit 675970
		break;
Packit 675970
	}
Packit 675970
}
Packit 675970
Packit 675970
static int make_nonblock(int fd) {
Packit 675970
	int fl;
Packit 675970
Packit 675970
	if ((fl = fcntl(fd, F_GETFL)) < 0)
Packit 675970
		return fl;
Packit 675970
Packit 675970
	if (fl & O_NONBLOCK)
Packit 675970
		return 0;
Packit 675970
Packit 675970
	return fcntl(fd, F_SETFL, fl | O_NONBLOCK);
Packit 675970
}
Packit 675970
Packit 675970
static int make_close_on_exec(int fd)
Packit 675970
{
Packit 675970
    return fcntl(fd, F_SETFD, FD_CLOEXEC);
Packit 675970
}
Packit 675970
Packit 675970
snd_pulse_t *pulse_new(void)
Packit 675970
{
Packit 675970
	snd_pulse_t *p;
Packit 675970
	int fd[2] = { -1, -1 };
Packit 675970
	char proc[PATH_MAX], buf[PATH_MAX + 20];
Packit 675970
Packit 675970
	p = calloc(1, sizeof(snd_pulse_t));
Packit 675970
Packit 675970
	if (!p)
Packit 675970
		return NULL;
Packit 675970
Packit 675970
	if (pipe(fd)) {
Packit 675970
		free(p);
Packit 675970
		return NULL;
Packit 675970
	}
Packit 675970
Packit 675970
	p->main_fd = fd[0];
Packit 675970
	p->thread_fd = fd[1];
Packit 675970
Packit 675970
	make_nonblock(p->main_fd);
Packit 675970
	make_close_on_exec(p->main_fd);
Packit 675970
	make_nonblock(p->thread_fd);
Packit 675970
	make_close_on_exec(p->thread_fd);
Packit 675970
Packit 675970
	p->mainloop = pa_threaded_mainloop_new();
Packit 675970
	if (!p->mainloop)
Packit 675970
		goto fail;
Packit 675970
Packit 675970
	if (pa_get_binary_name(proc, sizeof(proc)))
Packit 675970
		snprintf(buf, sizeof(buf), "ALSA plug-in [%s]",
Packit 675970
			 pa_path_get_filename(proc));
Packit 675970
	else
Packit 675970
		snprintf(buf, sizeof(buf), "ALSA plug-in");
Packit 675970
	buf[sizeof(buf)-1] = 0;
Packit 675970
Packit 675970
	p->context =
Packit 675970
	    pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), buf);
Packit 675970
Packit 675970
	if (!p->context)
Packit 675970
		goto fail;
Packit 675970
Packit 675970
	pa_context_set_state_callback(p->context, context_state_cb, p);
Packit 675970
Packit 675970
	if (pa_threaded_mainloop_start(p->mainloop) < 0)
Packit 675970
		goto fail;
Packit 675970
Packit 675970
	return p;
Packit 675970
Packit 675970
fail:
Packit 675970
	pulse_free(p);
Packit 675970
Packit 675970
	return NULL;
Packit 675970
}
Packit 675970
Packit 675970
void pulse_free(snd_pulse_t * p)
Packit 675970
{
Packit 675970
	if (p->mainloop)
Packit 675970
		pa_threaded_mainloop_stop(p->mainloop);
Packit 675970
Packit 675970
	if (p->context) {
Packit 675970
		pa_context_disconnect(p->context);
Packit 675970
		pa_context_unref(p->context);
Packit 675970
	}
Packit 675970
Packit 675970
	if (p->mainloop)
Packit 675970
		pa_threaded_mainloop_free(p->mainloop);
Packit 675970
Packit 675970
	if (p->thread_fd >= 0)
Packit 675970
		close(p->thread_fd);
Packit 675970
Packit 675970
	if (p->main_fd >= 0)
Packit 675970
		close(p->main_fd);
Packit 675970
Packit 675970
	free(p);
Packit 675970
}
Packit 675970
Packit 675970
int pulse_connect(snd_pulse_t * p, const char *server, int can_fallback)
Packit 675970
{
Packit 675970
	int err;
Packit 675970
	pa_context_flags_t flags;
Packit 675970
	pa_context_state_t state;
Packit 675970
Packit 675970
	assert(p);
Packit 675970
Packit 675970
	if (can_fallback)
Packit 675970
		flags = PA_CONTEXT_NOAUTOSPAWN;
Packit 675970
	else
Packit 675970
		flags = 0;
Packit 675970
Packit 675970
	if (!p->context || !p->mainloop)
Packit 675970
		return -EBADFD;
Packit 675970
Packit 675970
	state = pa_context_get_state(p->context);
Packit 675970
	if (state != PA_CONTEXT_UNCONNECTED)
Packit 675970
		return -EBADFD;
Packit 675970
Packit 675970
	pa_threaded_mainloop_lock(p->mainloop);
Packit 675970
Packit 675970
	err = pa_context_connect(p->context, server, flags, NULL);
Packit 675970
	if (err < 0)
Packit 675970
		goto error;
Packit 675970
Packit 675970
	for (;;) {
Packit 675970
		pa_context_state_t state = pa_context_get_state(p->context);
Packit 675970
Packit 675970
		if (!PA_CONTEXT_IS_GOOD(state))
Packit 675970
			goto error;
Packit 675970
Packit 675970
		if (state == PA_CONTEXT_READY)
Packit 675970
			break;
Packit 675970
Packit 675970
		pa_threaded_mainloop_wait(p->mainloop);
Packit 675970
	}
Packit 675970
Packit 675970
	pa_threaded_mainloop_unlock(p->mainloop);
Packit 675970
Packit 675970
	return 0;
Packit 675970
Packit 675970
      error:
Packit 675970
	if (!can_fallback)
Packit 675970
		SNDERR("PulseAudio: Unable to connect: %s\n",
Packit 675970
		       pa_strerror(pa_context_errno(p->context)));
Packit 675970
Packit 675970
	pa_threaded_mainloop_unlock(p->mainloop);
Packit 675970
Packit 675970
	return -ECONNREFUSED;
Packit 675970
}
Packit 675970
Packit 675970
void pulse_poll_activate(snd_pulse_t * p)
Packit 675970
{
Packit 675970
	static const char x = 'x';
Packit 675970
	assert(p);
Packit 675970
Packit 675970
	write(p->thread_fd, &x, 1);
Packit 675970
}
Packit 675970
Packit 675970
void pulse_poll_deactivate(snd_pulse_t * p)
Packit 675970
{
Packit 675970
	char buf[10];
Packit 675970
Packit 675970
	assert(p);
Packit 675970
Packit 675970
	/* Drain the pipe */
Packit 675970
	while (read(p->main_fd, buf, sizeof(buf)) > 0);
Packit 675970
}