openbsd-ports/audio/pulseaudio/files/module-sndio.c
eric 94a8e79e3b fix sndio backend: make mio_open blocking.
discussed with ratchov@ and armani@

ok ajacoutot@
2012-11-12 12:27:00 +00:00

820 lines
20 KiB
C

/* $OpenBSD: module-sndio.c,v 1.3 2012/11/12 12:27:00 eric Exp $ */
/*
* Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sndio.h>
#include "config.h"
#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/thread.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/poll.h>
#include "module-sndio-symdef.h"
#include "module-sndio-sysex.h"
/*
* TODO
*
* - handle latency correctly
* - make recording work correctly with playback
*/
PA_MODULE_AUTHOR("Eric Faurot");
PA_MODULE_DESCRIPTION("OpenBSD sndio sink/source");
PA_MODULE_VERSION("0.0");
PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"sink_properties=<properties for the sink> "
"source_name=<name for the source> "
"source_properties=<properties for the source> "
"device=<sndio device> "
"record=<enable source?> "
"playback=<enable sink?> "
"format=<sample format> "
"rate=<sample rate> "
"channels=<number of channels> "
"channel_map=<channel map> ");
static const char* const modargs[] = {
"sink_name",
"sink_properties",
"source_name",
"source_properties",
"device",
"record",
"playback",
"format",
"rate",
"channels",
"channel_map",
NULL
};
struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink;
pa_source *source;
pa_thread *thread;
pa_thread_mq thread_mq;
pa_rtpoll *rtpoll;
pa_rtpoll_item *rtpoll_item;
pa_memchunk memchunk;
struct sio_hdl *hdl;
struct sio_par par;
size_t bufsz;
int sink_running;
unsigned int volume;
pa_rtpoll_item *rtpoll_item_mio;
struct mio_hdl *mio;
int master;
int mst;
int midx;
int mlen;
int mready;
#define MSGMAX 0x100
uint8_t mmsg[MSGMAX];
};
static void
sndio_midi_message(struct userdata *u, const char *msg, size_t len)
{
struct sysex *x = (struct sysex *)msg;
if (len == SYSEX_SIZE(master) &&
x->start == SYSEX_START &&
x->type == SYSEX_TYPE_RT &&
x->id0 == SYSEX_CONTROL &&
x->id1 == SYSEX_MASTER) {
u->master = x->u.master.coarse;
pa_log_debug("MIDI master level is %i", u->master);
return;
}
if (len == SYSEX_SIZE(empty) &&
x->start == SYSEX_START &&
x->type == SYSEX_TYPE_EDU &&
x->id0 == SYSEX_AUCAT &&
x->id1 == SYSEX_AUCAT_DUMPEND) {
pa_log_debug("MIDI config done");
u->mready = 1;
return;
}
}
static void
sndio_midi_input(struct userdata *u, const unsigned char *buf, unsigned int len)
{
static unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
static unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
unsigned int c;
for (; len > 0; len--) {
c = *buf;
buf++;
if (c >= 0xf8) {
/* clock events not used yet */
} else if (c >= 0xf0) {
if (u->mst == SYSEX_START &&
c == SYSEX_END &&
u->midx < MSGMAX) {
u->mmsg[u->midx++] = c;
sndio_midi_message(u, u->mmsg, u->midx);
continue;
}
u->mmsg[0] = c;
u->mlen = common_len[c & 7];
u->mst = c;
u->midx = 1;
} else if (c >= 0x80) {
u->mmsg[0] = c;
u->mlen = voice_len[(c >> 4) & 7];
u->mst = c;
u->midx = 1;
} else if (u->mst) {
if (u->midx == MSGMAX)
continue;
if (u->midx == 0)
u->mmsg[u->midx++] = u->mst;
u->mmsg[u->midx++] = c;
if (u->midx == u->mlen) {
sndio_midi_message(u, u->mmsg, u->midx);
u->midx = 0;
}
}
}
}
static int
sndio_midi_setup(struct userdata *u)
{
static const unsigned char dumpreq[] = {
SYSEX_START,
SYSEX_TYPE_EDU,
0,
SYSEX_AUCAT,
SYSEX_AUCAT_DUMPREQ,
SYSEX_END
};
size_t s;
int r, n;
unsigned char buf[MSGMAX];
u->mio = mio_open("snd/0", MIO_IN | MIO_OUT, 0);
if (u->mio == NULL) {
pa_log("mio_open failed");
return (-1);
}
n = mio_nfds(u->mio);
u->rtpoll_item_mio = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, n);
if (u->rtpoll_item_mio == NULL) {
pa_log("could not allocate mio poll item");
return (-1);
}
s = mio_write(u->mio, dumpreq, sizeof(dumpreq));
pa_log_debug("mio_write: %zu / %zu", s, sizeof(dumpreq));
while (!u->mready) {
s = mio_read(u->mio, buf, sizeof buf);
pa_log_debug("mio_read: %zu", s);
if (s == 0) {
pa_log("mio_read()");
return (-1);
}
sndio_midi_input(u, buf, s);
}
return (0);
}
static void
sndio_on_volume(void *arg, unsigned int vol)
{
struct userdata *u = arg;
u->volume = vol;
}
static void
sndio_get_volume(pa_sink *s)
{
struct userdata *u = s->userdata;
int i;
uint32_t v;
if (u->master >= SIO_MAXVOL)
v = PA_VOLUME_NORM;
else
v = PA_CLAMP_VOLUME((u->volume * PA_VOLUME_NORM) / SIO_MAXVOL);
for (i = 0; i < s->real_volume.channels; i++)
s->real_volume.values[i] = v;
}
static void
sndio_set_volume(pa_sink *s)
{
struct userdata *u = s->userdata;
struct sysex msg;
unsigned int vol;
if (s->real_volume.values[0] >= PA_VOLUME_NORM)
vol = SIO_MAXVOL;
else
vol = (s->real_volume.values[0] * SIO_MAXVOL) / PA_VOLUME_NORM;
/*
sio_setvol(u->hdl, vol);
*/
msg.start = SYSEX_START;
msg.type = SYSEX_TYPE_RT;
msg.id0 = SYSEX_CONTROL;
msg.id1 = SYSEX_MASTER;
msg.u.master.fine = 0;
msg.u.master.coarse = vol;
msg.u.master.end = SYSEX_END;
/* XXX do better */
if (mio_write(u->mio, &msg, SYSEX_SIZE(master)) != SYSEX_SIZE(master))
pa_log("set_volume: couldn't write message");
}
static int
sndio_sink_message(pa_msgobject *o, int code, void *data, int64_t offset,
pa_memchunk *chunk)
{
struct userdata *u = PA_SINK(o)->userdata;
pa_sink_state_t state;
int ret;
pa_log_debug(
"sndio_sink_msg: obj=%p code=%i data=%p offset=%lli chunk=%p",
o, code, data, offset, chunk);
switch (code) {
case PA_SINK_MESSAGE_GET_LATENCY:
pa_log_debug("sink:PA_SINK_MESSAGE_GET_LATENCY");
*(pa_usec_t*)data = pa_bytes_to_usec(u->par.bufsz,
&u->sink->sample_spec);
return (0);
case PA_SINK_MESSAGE_SET_STATE:
pa_log_debug("sink:PA_SINK_MESSAGE_SET_STATE ");
state = (pa_sink_state_t)(data);
switch (state) {
case PA_SINK_SUSPENDED:
pa_log_debug("SUSPEND");
if (u->sink_running == 1)
sio_stop(u->hdl);
u->sink_running = 0;
break;
case PA_SINK_IDLE:
case PA_SINK_RUNNING:
pa_log_debug((code == PA_SINK_IDLE) ? "IDLE":"RUNNING");
if (u->sink_running == 0)
sio_start(u->hdl);
u->sink_running = 1;
break;
case PA_SINK_INVALID_STATE:
pa_log_debug("INVALID_STATE");
break;
case PA_SINK_UNLINKED:
pa_log_debug("UNLINKED");
break;
case PA_SINK_INIT:
pa_log_debug("INIT");
break;
}
break;
default:
pa_log_debug("sink:PA_SINK_???");
}
ret = pa_sink_process_msg(o, code, data, offset, chunk);
return (ret);
}
static int
sndio_source_message(pa_msgobject *o, int code, void *data, int64_t offset,
pa_memchunk *chunk)
{
struct userdata *u = PA_SOURCE(o)->userdata;
pa_source_state_t state;
int ret;
pa_log_debug(
"sndio_source_msg: obj=%p code=%i data=%p offset=%lli chunk=%p",
o, code, data, offset, chunk);
switch (code) {
case PA_SOURCE_MESSAGE_GET_LATENCY:
pa_log_debug("source:PA_SOURCE_MESSAGE_GET_LATENCY");
*(pa_usec_t*)data = pa_bytes_to_usec(u->bufsz,
&u->source->sample_spec);
return (0);
case PA_SOURCE_MESSAGE_SET_STATE:
pa_log_debug("source:PA_SOURCE_MESSAGE_SET_STATE ");
state = (pa_source_state_t)(data);
switch (state) {
case PA_SOURCE_SUSPENDED:
pa_log_debug("SUSPEND");
sio_stop(u->hdl);
break;
case PA_SOURCE_IDLE:
case PA_SOURCE_RUNNING:
pa_log_debug((code == PA_SOURCE_IDLE)?"IDLE":"RUNNING");
sio_start(u->hdl);
break;
case PA_SOURCE_INVALID_STATE:
pa_log_debug("INVALID_STATE");
break;
case PA_SOURCE_UNLINKED:
pa_log_debug("UNLINKED");
break;
case PA_SOURCE_INIT:
pa_log_debug("INIT");
break;
}
break;
default:
pa_log_debug("source:PA_SOURCE_???");
}
ret = pa_source_process_msg(o, code, data, offset, chunk);
return (ret);
}
static void
sndio_thread(void *arg)
{
struct userdata *u = arg;
int ret;
short revents, events;
struct pollfd *fds_sio, *fds_mio;
size_t w, r, l;
char *p;
char buf[256];
struct pa_memchunk memchunk;
pa_log_debug("sndio thread starting up");
pa_thread_mq_install(&u->thread_mq);
fds_sio = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
fds_mio = pa_rtpoll_item_get_pollfd(u->rtpoll_item_mio, NULL);
revents = 0;
for (;;) {
pa_log_debug("sndio_thread: loop");
/* ??? oss does that. */
if (u->sink
&& PA_SINK_IS_OPENED(u->sink->thread_info.state)
&& u->sink->thread_info.rewind_requested)
pa_sink_process_rewind(u->sink, 0);
if (u->sink &&
PA_SINK_IS_OPENED(u->sink->thread_info.state)
&& (revents & POLLOUT)) {
if (u->memchunk.length <= 0)
pa_sink_render(u->sink, u->bufsz, &u->memchunk);
p = pa_memblock_acquire(u->memchunk.memblock);
w = sio_write(u->hdl, p + u->memchunk.index,
u->memchunk.length);
pa_memblock_release(u->memchunk.memblock);
pa_log_debug("wrote %zu bytes of %zu", w,
u->memchunk.length);
u->memchunk.index += w;
u->memchunk.length -= w;
if (u->memchunk.length <= 0) {
pa_memblock_unref(u->memchunk.memblock);
pa_memchunk_reset(&u->memchunk);
}
}
if (u->source &&
PA_SOURCE_IS_OPENED(u->source->thread_info.state)
&& (revents & POLLIN)) {
memchunk.memblock = pa_memblock_new(u->core->mempool,
(size_t) -1);
l = pa_memblock_get_length(memchunk.memblock);
if (l > u->bufsz)
l = u->bufsz;
p = pa_memblock_acquire(memchunk.memblock);
r = sio_read(u->hdl, p, l);
pa_memblock_release(memchunk.memblock);
pa_log_debug("read %zu bytes of %zu", r, l);
memchunk.index = 0;
memchunk.length = r;
pa_source_post(u->source, &memchunk);
pa_memblock_unref(memchunk.memblock);
}
events = 0;
if (u->source &&
PA_SOURCE_IS_OPENED(u->source->thread_info.state))
events |= POLLIN;
if (u->sink &&
PA_SINK_IS_OPENED(u->sink->thread_info.state))
events |= POLLOUT;
sio_pollfd(u->hdl, fds_sio, events);
mio_pollfd(u->mio, fds_mio, POLLIN);
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
if (ret == 0)
goto finish;
revents = mio_revents(u->mio, fds_mio);
if (revents & POLLHUP) {
pa_log("mio POLLHUP!");
break;
}
if (revents && POLLIN) {
r = mio_read(u->mio, buf, sizeof buf);
if (mio_eof(u->mio)) {
pa_log("mio error");
break;
}
if (r)
sndio_midi_input(u, buf, r);
}
revents = sio_revents(u->hdl, fds_sio);
pa_log_debug("sndio_thread: loop ret=%i, revents=%x", ret,
(int)revents);
if (revents & POLLHUP) {
pa_log("POLLHUP!");
break;
}
}
fail:
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core),
PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
finish:
pa_log_debug("sndio thread shutting down");
}
int
pa__init(pa_module *m)
{
pa_modargs *ma = NULL;
pa_bool_t record = TRUE, playback = TRUE;
pa_sample_spec ss;
pa_channel_map map;
pa_sink_new_data sink;
pa_source_new_data source;
struct sio_par par;
unsigned int mode = 0;
char buf[256];
const char *name, *dev;
struct userdata *u = NULL;
int nfds;
struct pollfd;
if ((u = calloc(1, sizeof(struct userdata))) == NULL) {
pa_log("Failed to allocate userdata");
goto fail;
}
m->userdata = u;
u->core = m->core;
u->module = m;
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
if (!(ma = pa_modargs_new(m->argument, modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 ||
pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
pa_log("record= and playback= expect boolean argument");
goto fail;
}
if (playback)
mode |= SIO_PLAY;
if (record)
mode |= SIO_REC;
if (!mode) {
pa_log("Neither playback nor record enabled for device");
goto fail;
}
dev = pa_modargs_get_value(ma, "device", NULL);
if ((u->hdl = sio_open(dev, mode, 1)) == NULL) {
pa_log("Cannot open sndio device.");
goto fail;
}
ss = m->core->default_sample_spec;
map = m->core->default_channel_map;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map,
PA_CHANNEL_MAP_OSS) < 0) {
pa_log("Failed to parse sample specification or channel map");
goto fail;
}
sio_initpar(&par);
par.rate = ss.rate;
par.pchan = (mode & SIO_PLAY) ? ss.channels : 0;
par.rchan = (mode & SIO_REC) ? ss.channels : 0;
par.sig = 1;
switch (ss.format) {
case PA_SAMPLE_U8:
par.bits = 8;
par.bps = 1;
par.sig = 0;
break;
case PA_SAMPLE_S16LE:
case PA_SAMPLE_S16BE:
par.bits = 16;
par.bps = 2;
par.le = (ss.format == PA_SAMPLE_S16LE) ? 1 : 0;
break;
case PA_SAMPLE_S32LE:
case PA_SAMPLE_S32BE:
par.bits = 32;
par.bps = 4;
par.le = (ss.format == PA_SAMPLE_S32LE) ? 1 : 0;
break;
case PA_SAMPLE_S24LE:
case PA_SAMPLE_S24BE:
par.bits = 24;
par.bps = 3;
par.le = (ss.format == PA_SAMPLE_S24LE) ? 1 : 0;
break;
case PA_SAMPLE_S24_32LE:
case PA_SAMPLE_S24_32BE:
par.bits = 24;
par.bps = 4;
par.le = (ss.format == PA_SAMPLE_S24_32LE) ? 1 : 0;
par.msb = 0; /* XXX check this */
break;
case PA_SAMPLE_ALAW:
case PA_SAMPLE_ULAW:
case PA_SAMPLE_FLOAT32LE:
case PA_SAMPLE_FLOAT32BE:
default:
pa_log("Unsupported sample format");
goto fail;
}
/* XXX what to do with channel map? */
if (sio_setpar(u->hdl, &par) == -1) {
pa_log("Could not set requested parameters");
goto fail;
}
if (sio_getpar(u->hdl, &u->par) == -1) {
pa_log("Could not retreive parameters");
goto fail;
}
if (u->par.rate != par.rate)
pa_log_warn("rate changed: %u -> %u", par.rate, u->par.rate);
if (u->par.pchan != par.pchan)
pa_log_warn("playback channels changed: %u -> %u",
par.rchan, u->par.rchan);
if (u->par.rchan != par.rchan)
pa_log_warn("record channels changed: %u -> %u",
par.rchan, u->par.rchan);
/* XXX check sample format */
ss.rate = u->par.rate;
ss.channels = (mode & SIO_PLAY) ? u->par.pchan : u->par.rchan;
/* XXX what to do with map? */
u->bufsz = u->par.bufsz * u->par.bps * u->par.pchan;
nfds = sio_nfds(u->hdl);
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, nfds);
if (u->rtpoll_item == NULL) {
pa_log("could not allocate poll item");
goto fail;
}
if (mode & SIO_PLAY) {
pa_sink_new_data_init(&sink);
sink.driver = __FILE__;
sink.module = m;
sink.namereg_fail = TRUE;
name = pa_modargs_get_value(ma, "sink_name", NULL);
if (name == NULL) {
sink.namereg_fail = FALSE;
snprintf(buf, sizeof (buf), "sndio-sink");
name = buf;
}
pa_sink_new_data_set_name(&sink, name);
pa_sink_new_data_set_sample_spec(&sink, &ss);
pa_sink_new_data_set_channel_map(&sink, &map);
pa_proplist_sets(sink.proplist,
PA_PROP_DEVICE_STRING, dev ? dev : "default");
pa_proplist_sets(sink.proplist,
PA_PROP_DEVICE_API, "sndio");
pa_proplist_sets(sink.proplist,
PA_PROP_DEVICE_DESCRIPTION, dev ? dev : "default");
pa_proplist_sets(sink.proplist,
PA_PROP_DEVICE_ACCESS_MODE, "serial");
/*
pa_proplist_setf(sink.proplist,
PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%u",
u->par.bufsz * u->par.bps * u->par.pchan);
*/
/*
pa_proplist_setf(sink.proplist,
PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu",
(unsigned long) (u->out_fragment_size));
*/
if (pa_modargs_get_proplist(ma, "sink_properties",
sink.proplist, PA_UPDATE_REPLACE) < 0) {
pa_log("Invalid sink properties");
pa_sink_new_data_done(&sink);
goto fail;
}
u->sink = pa_sink_new(m->core, &sink, PA_SINK_LATENCY);
pa_sink_new_data_done(&sink);
if (u->sink == NULL) {
pa_log("Failed to create sync");
goto fail;
}
u->sink->userdata = u;
u->sink->parent.process_msg = sndio_sink_message;
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
pa_sink_set_fixed_latency(u->sink,
pa_bytes_to_usec(u->bufsz, &u->sink->sample_spec));
sio_onvol(u->hdl, sndio_on_volume, u);
pa_sink_set_get_volume_callback(u->sink, sndio_get_volume);
pa_sink_set_set_volume_callback(u->sink, sndio_set_volume);
u->sink->n_volume_steps = SIO_MAXVOL + 1;
}
if (mode & SIO_REC) {
pa_source_new_data_init(&source);
source.driver = __FILE__;
source.module = m;
source.namereg_fail = TRUE;
name = pa_modargs_get_value(ma, "source_name", NULL);
if (name == NULL) {
source.namereg_fail = FALSE;
snprintf(buf, sizeof (buf), "sndio-source");
name = buf;
}
pa_source_new_data_set_name(&source, name);
pa_source_new_data_set_sample_spec(&source, &ss);
pa_source_new_data_set_channel_map(&source, &map);
pa_proplist_sets(source.proplist,
PA_PROP_DEVICE_STRING, dev ? dev : "default");
pa_proplist_sets(source.proplist,
PA_PROP_DEVICE_API, "sndio");
pa_proplist_sets(source.proplist,
PA_PROP_DEVICE_DESCRIPTION, dev ? dev : "default");
pa_proplist_sets(source.proplist,
PA_PROP_DEVICE_ACCESS_MODE, "serial");
pa_proplist_setf(source.proplist,
PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%u",
u->par.bufsz * u->par.bps * u->par.rchan);
/*
pa_proplist_setf(source.proplist,
PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu",
(unsigned long) (u->in_fragment_size));
*/
if (pa_modargs_get_proplist(ma, "source_properties",
source.proplist, PA_UPDATE_REPLACE) < 0) {
pa_log("Invalid source properties");
pa_source_new_data_done(&source);
goto fail;
}
u->source = pa_source_new(m->core, &source, PA_SOURCE_LATENCY);
pa_source_new_data_done(&source);
if (u->source == NULL) {
pa_log("Failed to create source");
goto fail;
}
u->source->userdata = u;
u->source->parent.process_msg = sndio_source_message;
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
/*
pa_source_set_fixed_latency(u->source,
pa_bytes_to_usec(u->in_hwbuf_size, &u->source->sample_spec));
*/
}
pa_log_debug("buffer: frame=%u bytes=%zu msec=%u", u->par.bufsz,
u->bufsz, pa_bytes_to_usec(u->bufsz, &u->sink->sample_spec));
pa_memchunk_reset(&u->memchunk);
if (sndio_midi_setup(u) == -1)
goto fail;
if ((u->thread = pa_thread_new("sndio", sndio_thread, u)) == NULL) {
pa_log("Failed to create sndio thread.");
goto fail;
}
if (u->sink)
pa_sink_put(u->sink);
if (u->source)
pa_source_put(u->source);
pa_modargs_free(ma);
return (0);
fail:
if (u)
pa__done(m);
if (ma)
pa_modargs_free(ma);
return (-1);
}
void
pa__done(pa_module *m)
{
struct userdata *u;
if (!(u = m->userdata))
return;
if (u->sink)
pa_sink_unlink(u->sink);
if (u->source)
pa_source_unlink(u->source);
if (u->thread) {
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN,
NULL, 0, NULL);
pa_thread_free(u->thread);
}
pa_thread_mq_done(&u->thread_mq);
if (u->sink)
pa_sink_unref(u->sink);
if (u->source)
pa_source_unref(u->source);
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
if (u->rtpoll_item)
pa_rtpoll_item_free(u->rtpoll_item);
if (u->rtpoll_item_mio)
pa_rtpoll_item_free(u->rtpoll_item_mio);
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
if (u->hdl)
sio_close(u->hdl);
if (u->mio)
mio_close(u->mio);
free(u);
}