|
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 |
}
|