summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorFinn Thain <fthain@telegraphics.com.au>2009-02-26 16:48:58 +1100
committerLennart Poettering <lennart@poettering.net>2009-03-03 22:27:00 +0100
commit0329edd1791e3c8fbed33f266d86cae6b91a5556 (patch)
treeb953cef1450d5aa7763e8d6b16d1c7f100d82c31 /src
parentff38eaf67773e0039befb95c0f9ad91e7a06fc3f (diff)
revive solaris module
Hi All, This patch fixes the solaris audio device source and sink, and fixes some portability issues that break the build on solaris. Questions and comments welcomed. I've tested this patch only with OpenSolaris Express snv 103. Eventually I hope to be able to test a few older releases and older hardware (though it is hard to say whether there is much interest in those). This is my first brush with pulseaudio and so I read the wiki docs and some of the source code but I'm still unsure of a few things. In particular I'm wondering about rewind processing, corking and what (if anything) the module needs for those. I'm also unclear on the implications of thread_info.buffer_size, .fragment_size and .max_request, and whether my code is correct or not. This patch disables link map/library versioning unless ld is GNU ld. Another approach for solaris would be to use that linker's -M option, but I couldn't make that work (due to undefined mainloop, browse and simple symbols when linking pacat. I can post the errors if anyone is intested.) Thanks, Finn Thain
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am20
-rw-r--r--src/daemon/daemon-conf.c10
-rw-r--r--src/daemon/daemon-conf.h6
-rw-r--r--src/daemon/main.c2
-rw-r--r--src/modules/module-solaris.c957
-rw-r--r--src/modules/raop/raop_client.c5
-rw-r--r--src/modules/rtp/rtsp_client.c7
-rw-r--r--src/pulsecore/core-util.c2
-rw-r--r--src/pulsecore/rtclock.c5
9 files changed, 670 insertions, 344 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 9f2fa02a..aa190a45 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -713,8 +713,11 @@ libpulse_la_SOURCES = \
pulse/xmalloc.c pulse/xmalloc.h
libpulse_la_CFLAGS = $(AM_CFLAGS)
-libpulse_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINORMICRO@.la
+libpulse_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO)
+if HAVE_GNU_LD
+libpulse_la_LDFLAGS += -Wl,-version-script=$(srcdir)/map-file
+endif
if HAVE_X11
libpulse_la_SOURCES += pulse/client-conf-x11.c pulse/client-conf-x11.h
@@ -725,17 +728,26 @@ endif
libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h
libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la
-libpulse_simple_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_SIMPLE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+libpulse_simple_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_SIMPLE_VERSION_INFO)
+if HAVE_GNU_LD
+libpulse_simple_la_LDFLAGS += -Wl,-version-script=$(srcdir)/map-file
+endif
libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h
libpulse_browse_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(AVAHI_LIBS)
-libpulse_browse_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_BROWSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+libpulse_browse_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_BROWSE_VERSION_INFO)
+if HAVE_GNU_LD
+libpulse_browse_la_LDFLAGS += -Wl,-version-script=$(srcdir)/map-file
+endif
libpulse_mainloop_glib_la_SOURCES = pulse/glib-mainloop.h pulse/glib-mainloop.c
libpulse_mainloop_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS)
libpulse_mainloop_glib_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(GLIB20_LIBS)
-libpulse_mainloop_glib_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+libpulse_mainloop_glib_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO)
+if HAVE_GNU_LD
+libpulse_mainloop_glib_la_LDFLAGS += -Wl,-version-script=$(srcdir)/map-file
+endif
###################################
# OSS emulation #
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index b02377ab..ac6cc8aa 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -94,8 +94,10 @@ static const pa_daemon_conf default_conf = {
,.rlimit_fsize = { .value = 0, .is_set = FALSE },
.rlimit_data = { .value = 0, .is_set = FALSE },
.rlimit_stack = { .value = 0, .is_set = FALSE },
- .rlimit_core = { .value = 0, .is_set = FALSE },
- .rlimit_rss = { .value = 0, .is_set = FALSE }
+ .rlimit_core = { .value = 0, .is_set = FALSE }
+#ifdef RLIMIT_RSS
+ ,.rlimit_rss = { .value = 0, .is_set = FALSE }
+#endif
#ifdef RLIMIT_NPROC
,.rlimit_nproc = { .value = 0, .is_set = FALSE }
#endif
@@ -472,7 +474,9 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "rlimit-data", parse_rlimit, &c->rlimit_data, NULL },
{ "rlimit-stack", parse_rlimit, &c->rlimit_stack, NULL },
{ "rlimit-core", parse_rlimit, &c->rlimit_core, NULL },
+#ifdef RLIMIT_RSS
{ "rlimit-rss", parse_rlimit, &c->rlimit_rss, NULL },
+#endif
#ifdef RLIMIT_NOFILE
{ "rlimit-nofile", parse_rlimit, &c->rlimit_nofile, NULL },
#endif
@@ -651,7 +655,9 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
+#ifdef RLIMIT_RSS
pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
+#endif
#ifdef RLIMIT_AS
pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
#endif
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 787676c7..9cec189f 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -87,8 +87,10 @@ typedef struct pa_daemon_conf {
char *config_file;
#ifdef HAVE_SYS_RESOURCE_H
- pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss;
-
+ pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core;
+#ifdef RLIMIT_RSS
+ pa_rlimit rlimit_rss;
+#endif
#ifdef RLIMIT_NOFILE
pa_rlimit rlimit_nofile;
#endif
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 46a279d0..0048e7b7 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -294,7 +294,9 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {
set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK");
set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
+#ifdef RLIMIT_RSS
set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS");
+#endif
#ifdef RLIMIT_NPROC
set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
#endif
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index 738115c5..783c2440 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -3,6 +3,7 @@
Copyright 2006 Lennart Poettering
Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ Copyright 2009 Finn Thain
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -44,6 +45,7 @@
#include <pulse/mainloop-signal.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
+#include <pulse/util.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/sink.h>
@@ -57,22 +59,25 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/thread.h>
+#include <pulsecore/rtclock.h>
#include "module-solaris-symdef.h"
-PA_MODULE_AUTHOR("Pierre Ossman")
-PA_MODULE_DESCRIPTION("Solaris Sink/Source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Pierre Ossman");
+PA_MODULE_DESCRIPTION("Solaris Sink/Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"source_name=<name for the source> "
- "device=<OSS device> record=<enable source?> "
+ "device=<audio device file name> "
+ "record=<enable source?> "
"playback=<enable sink?> "
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
"buffer_size=<record buffer size> "
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
+PA_MODULE_LOAD_ONCE(FALSE);
struct userdata {
pa_core *core;
@@ -87,15 +92,24 @@ struct userdata {
pa_memchunk memchunk;
- unsigned int page_size;
-
uint32_t frame_size;
- uint32_t buffer_size;
- unsigned int written_bytes, read_bytes;
+ int32_t buffer_size;
+ volatile uint64_t written_bytes, read_bytes;
+ pa_mutex *written_bytes_lock;
+ char *device_name;
+ int mode;
int fd;
pa_rtpoll_item *rtpoll_item;
pa_module *module;
+
+ pa_bool_t sink_suspended, source_suspended;
+
+ uint32_t play_samples_msw, record_samples_msw;
+ uint32_t prev_playback_samples, prev_record_samples;
+ pa_mutex *sample_counter_lock;
+
+ size_t min_request;
};
static const char* const valid_modargs[] = {
@@ -112,89 +126,303 @@ static const char* const valid_modargs[] = {
NULL
};
-#define DEFAULT_SINK_NAME "solaris_output"
-#define DEFAULT_SOURCE_NAME "solaris_input"
#define DEFAULT_DEVICE "/dev/audio"
+#define MIN_BUFFER_SIZE (640)
+#define MAX_RENDER_HZ (300)
-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;
+/* This render rate limit implies a minimum latency, but without it we waste too much CPU time in the
+ * IO thread. The maximum render rate and minimum latency (or minimum buffer size) are unachievable on
+ * common hardware anyway. Note that MIN_BUFFER_SIZE * MAX_RENDER_HZ >= 4 * 48000 Bps.
+ */
+
+static uint64_t get_playback_buffered_bytes(struct userdata *u) {
+ audio_info_t info;
+ uint64_t played_bytes;
int err;
+
+ pa_assert(u->sink);
+
+ pa_mutex_lock(u->sample_counter_lock);
+
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ pa_assert(err >= 0);
+
+ /* Handle wrap-around of the device's sample counter, which is a uint_32. */
+ if (u->prev_playback_samples > info.play.samples) {
+ /* Unfortunately info.play.samples can sometimes go backwards, even before it wraps! */
+ if (u->prev_playback_samples + info.play.samples < 240000) {
+ ++u->play_samples_msw;
+ } else {
+ pa_log_debug("play.samples went backwards %d bytes", u->prev_playback_samples - info.play.samples);
+ }
+ }
+ u->prev_playback_samples = info.play.samples;
+ played_bytes = (((uint64_t)u->play_samples_msw << 32) + info.play.samples) * u->frame_size;
+
+ pa_mutex_unlock(u->sample_counter_lock);
+
+ return u->written_bytes - played_bytes;
+}
+
+static pa_usec_t sink_get_latency(struct userdata *u, pa_sample_spec *ss) {
+ pa_usec_t r = 0;
+
+ pa_assert(u);
+ pa_assert(ss);
+
+ if (u->fd >= 0) {
+ pa_mutex_lock(u->written_bytes_lock);
+ r = pa_bytes_to_usec(get_playback_buffered_bytes(u), ss);
+ if (u->memchunk.memblock)
+ r += pa_bytes_to_usec(u->memchunk.length, ss);
+ pa_mutex_unlock(u->written_bytes_lock);
+ }
+ return r;
+}
+
+static uint64_t get_recorded_bytes(struct userdata *u) {
audio_info_t info;
+ uint64_t result;
+ int err;
- switch (code) {
- case PA_SINK_MESSAGE_GET_LATENCY: {
- pa_usec_t r = 0;
+ pa_assert(u->source);
- if (u->fd >= 0) {
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ pa_assert(err >= 0);
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- pa_assert(err >= 0);
+ if (u->prev_record_samples > info.record.samples)
+ ++u->record_samples_msw;
+ u->prev_record_samples = info.record.samples;
+ result = (((uint64_t)u->record_samples_msw << 32) + info.record.samples) * u->frame_size;
- r += pa_bytes_to_usec(u->written_bytes, &PA_SINK(o)->sample_spec);
- r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &PA_SINK(o)->sample_spec);
+ return result;
+}
- if (u->memchunk.memblock)
- r += pa_bytes_to_usec(u->memchunk.length, &PA_SINK(o)->sample_spec);
- }
+static pa_usec_t source_get_latency(struct userdata *u, pa_sample_spec *ss) {
+ pa_usec_t r = 0;
+ audio_info_t info;
+
+ pa_assert(u);
+ pa_assert(ss);
- *((pa_usec_t*) data) = r;
+ if (u->fd) {
+ int err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ pa_assert(err >= 0);
- return 0;
+ r = pa_bytes_to_usec(get_recorded_bytes(u), ss) - pa_bytes_to_usec(u->read_bytes, ss);
}
+ return r;
+}
- case PA_SINK_MESSAGE_SET_VOLUME:
- if (u->fd >= 0) {
- AUDIO_INITINFO(&info);
+static void build_pollfd(struct userdata *u) {
+ struct pollfd *pollfd;
- info.play.gain = pa_cvolume_avg((pa_cvolume*)data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
- assert(info.play.gain <= AUDIO_MAX_GAIN);
+ pa_assert(u);
+ pa_assert(!u->rtpoll_item);
+ u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
- if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
- if (errno == EINVAL)
- pa_log("AUDIO_SETINFO: Unsupported volume.");
- else
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
- } else {
- return 0;
- }
- }
- break;
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+ pollfd->fd = u->fd;
+ pollfd->events = 0;
+ pollfd->revents = 0;
+}
- case PA_SINK_MESSAGE_GET_VOLUME:
- if (u->fd >= 0) {
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- assert(err >= 0);
+static int set_buffer(int fd, int buffer_size) {
+ audio_info_t info;
- pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
- info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+ pa_assert(fd >= 0);
- return 0;
- }
- break;
+ AUDIO_INITINFO(&info);
+ info.play.buffer_size = buffer_size;
+ info.record.buffer_size = buffer_size;
- case PA_SINK_MESSAGE_SET_MUTE:
- if (u->fd >= 0) {
- AUDIO_INITINFO(&info);
+ if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
+ if (errno == EINVAL)
+ pa_log("AUDIO_SETINFO: Unsupported buffer size.");
+ else
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ return -1;
+ }
- info.output_muted = !!PA_PTR_TO_UINT(data);
+ return 0;
+}
- if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
- else
- return 0;
+static int auto_format(int fd, int mode, pa_sample_spec *ss) {
+ audio_info_t info;
+
+ pa_assert(fd >= 0);
+ pa_assert(ss);
+
+ AUDIO_INITINFO(&info);
+
+ if (mode != O_RDONLY) {
+ info.play.sample_rate = ss->rate;
+ info.play.channels = ss->channels;
+ switch (ss->format) {
+ case PA_SAMPLE_U8:
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ break;
+ case PA_SAMPLE_ALAW:
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ALAW;
+ break;
+ case PA_SAMPLE_ULAW:
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ break;
+ case PA_SAMPLE_S16NE:
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ break;
+ default:
+ pa_log("AUDIO_SETINFO: Unsupported sample format.");
+ return -1;
}
- break;
+ }
- case PA_SINK_MESSAGE_GET_MUTE:
- if (u->fd >= 0) {
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- pa_assert(err >= 0);
+ if (mode != O_WRONLY) {
+ info.record.sample_rate = ss->rate;
+ info.record.channels = ss->channels;
+ switch (ss->format) {
+ case PA_SAMPLE_U8:
+ info.record.precision = 8;
+ info.record.encoding = AUDIO_ENCODING_LINEAR;
+ break;
+ case PA_SAMPLE_ALAW:
+ info.record.precision = 8;
+ info.record.encoding = AUDIO_ENCODING_ALAW;
+ break;
+ case PA_SAMPLE_ULAW:
+ info.record.precision = 8;
+ info.record.encoding = AUDIO_ENCODING_ULAW;
+ break;
+ case PA_SAMPLE_S16NE:
+ info.record.precision = 16;
+ info.record.encoding = AUDIO_ENCODING_LINEAR;
+ break;
+ default:
+ pa_log("AUDIO_SETINFO: Unsupported sample format.");
+ return -1;
+ }
+ }
+
+ if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
+ if (errno == EINVAL)
+ pa_log("AUDIO_SETINFO: Failed to set sample format.");
+ else
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int open_audio_device(struct userdata *u, pa_sample_spec *ss) {
+ pa_assert(u);
+ pa_assert(ss);
+
+ if ((u->fd = open(u->device_name, u->mode | O_NONBLOCK)) < 0) {
+ pa_log_warn("open %s failed (%s)", u->device_name, pa_cstrerror(errno));
+ return -1;
+ }
+
+ pa_log_info("device opened in %s mode.", u->mode == O_WRONLY ? "O_WRONLY" : (u->mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+
+ if (auto_format(u->fd, u->mode, ss) < 0)
+ return -1;
+
+ if (set_buffer(u->fd, u->buffer_size) < 0)
+ return -1;
+
+ u->written_bytes = u->read_bytes = 0;
+ u->play_samples_msw = u->record_samples_msw = 0;
+ u->prev_playback_samples = u->prev_record_samples = 0;
+
+ return u->fd;
+}
+
+static int suspend(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->fd >= 0);
+
+ pa_log_info("Suspending...");
+
+ ioctl(u->fd, AUDIO_DRAIN, NULL);
+ pa_close(u->fd);
+ u->fd = -1;
+
+ if (u->rtpoll_item) {
+ pa_rtpoll_item_free(u->rtpoll_item);
+ u->rtpoll_item = NULL;
+ }
+
+ pa_log_info("Device suspended.");
+
+ return 0;
+}
+
+static int unsuspend(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->fd < 0);
- *(int*)data = !!info.output_muted;
+ pa_log_info("Resuming...");
+ if (open_audio_device(u, u->sink ? &u->sink->sample_spec : &u->source->sample_spec) < 0)
+ return -1;
+
+ build_pollfd(u);
+
+ pa_log_info("Device resumed.");
+
+ return 0;
+}
+
+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;
+
+ switch (code) {
+
+ case PA_SINK_MESSAGE_GET_LATENCY:
+ *((pa_usec_t*) data) = sink_get_latency(u, &PA_SINK(o)->sample_spec);
return 0;
- }
- break;
+
+ case PA_SINK_MESSAGE_SET_STATE:
+
+ switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+ case PA_SINK_SUSPENDED:
+
+ pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+ if (!u->source || u->source_suspended) {
+ if (suspend(u) < 0)
+ return -1;
+ }
+ u->sink_suspended = TRUE;
+ break;
+
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+
+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+ if (!u->source || u->source_suspended) {
+ if (unsuspend(u) < 0)
+ return -1;
+ u->sink->get_volume(u->sink);
+ u->sink->get_mute(u->sink);
+ }
+ u->sink_suspended = FALSE;
+ }
+ break;
+
+ case PA_SINK_INVALID_STATE:
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ ;
+ }
+
+ break;
}
return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -202,95 +430,168 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SOURCE(o)->userdata;
- int err;
- audio_info_t info;
switch (code) {
- case PA_SOURCE_MESSAGE_GET_LATENCY: {
- pa_usec_t r = 0;
- if (u->fd) {
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- pa_assert(err >= 0);
+ case PA_SOURCE_MESSAGE_GET_LATENCY:
+ *((pa_usec_t*) data) = source_get_latency(u, &PA_SOURCE(o)->sample_spec);
+ return 0;
- r += pa_bytes_to_usec(info.record.samples * u->frame_size, &PA_SOURCE(o)->sample_spec);
- r -= pa_bytes_to_usec(u->read_bytes, &PA_SOURCE(o)->sample_spec);
- }
+ case PA_SOURCE_MESSAGE_SET_STATE:
- *((pa_usec_t*) data) = r;
+ switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
- return 0;
- }
+ case PA_SOURCE_SUSPENDED:
- case PA_SOURCE_MESSAGE_SET_VOLUME:
- if (u->fd >= 0) {
- AUDIO_INITINFO(&info);
+ pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
- info.record.gain = pa_cvolume_avg((pa_cvolume*) data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
- assert(info.record.gain <= AUDIO_MAX_GAIN);
+ if (!u->sink || u->sink_suspended) {
+ if (suspend(u) < 0)
+ return -1;
+ }
+ u->source_suspended = TRUE;
+ break;
+
+ case PA_SOURCE_IDLE:
+ case PA_SOURCE_RUNNING:
+
+ if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
+ if (!u->sink || u->sink_suspended) {
+ if (unsuspend(u) < 0)
+ return -1;
+ u->source->get_volume(u->source);
+ }
+ u->source_suspended = FALSE;
+ }
+ break;
+
+ case PA_SOURCE_UNLINKED:
+ case PA_SOURCE_INIT:
+ case PA_SOURCE_INVALID_STATE:
+ ;
- if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
- if (errno == EINVAL)
- pa_log("AUDIO_SETINFO: Unsupported volume.");
- else
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
- } else {
- return 0;
- }
}
break;
- case PA_SOURCE_MESSAGE_GET_VOLUME:
- if (u->fd >= 0) {
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- pa_assert(err >= 0);
+ }
- pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
- info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+ return pa_source_process_msg(o, code, data, offset, chunk);
+}
- return 0;
- }
- break;
+static void sink_set_volume(pa_sink *s) {
+ struct userdata *u;
+ audio_info_t info;
+
+ pa_assert_se(u = s->userdata);
+
+ if (u->fd >= 0) {
+ AUDIO_INITINFO(&info);
+
+ info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+ assert(info.play.gain <= AUDIO_MAX_GAIN);
+
+ if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
+ if (errno == EINVAL)
+ pa_log("AUDIO_SETINFO: Unsupported volume.");
+ else
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ }
}
+}
- return pa_source_process_msg(o, code, data, offset, chunk);
+static void sink_get_volume(pa_sink *s) {
+ struct userdata *u;
+ audio_info_t info;
+
+ pa_assert_se(u = s->userdata);
+
+ if (u->fd >= 0) {
+ if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ else
+ pa_cvolume_set(&s->virtual_volume, s->sample_spec.channels,
+ info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+ }
}
-static void clear_underflow(struct userdata *u)
-{
+static void source_set_volume(pa_source *s) {
+ struct userdata *u;
audio_info_t info;
- AUDIO_INITINFO(&info);
+ pa_assert_se(u = s->userdata);
+
+ if (u->fd >= 0) {
+ AUDIO_INITINFO(&info);
- info.play.error = 0;
+ info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+ assert(info.play.gain <= AUDIO_MAX_GAIN);
- if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
+ if (errno == EINVAL)
+ pa_log("AUDIO_SETINFO: Unsupported volume.");
+ else
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ }
+ }
}
-static void clear_overflow(struct userdata *u)
-{
+static void source_get_volume(pa_source *s) {
+ struct userdata *u;
audio_info_t info;
- AUDIO_INITINFO(&info);
+ pa_assert_se(u = s->userdata);
- info.record.error = 0;
+ if (u->fd >= 0) {
+ if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ else
+ pa_cvolume_set(&s->virtual_volume, s->sample_spec.channels,
+ info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+ }
+}
- if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+static void sink_set_mute(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ audio_info_t info;
+
+ pa_assert(u);
+
+ if (u->fd >= 0) {
+ AUDIO_INITINFO(&info);
+
+ info.output_muted = !!s->muted;
+
+ if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ }
+}
+
+static void sink_get_mute(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ audio_info_t info;
+
+ pa_assert(u);
+
+ if (u->fd >= 0) {
+ if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ else
+ s->muted = !!info.output_muted;
+ }
}
static void thread_func(void *userdata) {
struct userdata *u = userdata;
unsigned short revents = 0;
- int ret;
+ int ret, err;
+ audio_info_t info;
pa_assert(u);
pa_log_debug("Thread starting up");
- if (u->core->high_priority)
- pa_make_realtime();
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
@@ -298,139 +599,158 @@ static void thread_func(void *userdata) {
for (;;) {
/* Render some data and write it to the dsp */
- if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) {
- audio_info_t info;
- int err;
- size_t len;
+ if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+ pa_usec_t xtime0;
+ uint64_t buffered_bytes;
+
+ if (u->sink->thread_info.rewind_requested)
+ pa_sink_process_rewind(u->sink, 0);
err = ioctl(u->fd, AUDIO_GETINFO, &info);
pa_assert(err >= 0);
- /*
- * Since we cannot modify the size of the output buffer we fake it
- * by not filling it more than u->buffer_size.
- */
- len = u->buffer_size;
- len -= u->written_bytes - (info.play.samples * u->frame_size);
-
- /* The sample counter can sometimes go backwards :( */
- if (len > u->buffer_size)
- len = 0;
-
if (info.play.error) {
- pa_log_debug("Solaris buffer underflow!");
- clear_underflow(u);
- }
+ pa_log_debug("buffer under-run!");
- len -= len % u->frame_size;
+ AUDIO_INITINFO(&info);
+ info.play.error = 0;
+ if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ }
- while (len) {
+ for (;;) {
void *p;
- ssize_t r;
+ ssize_t w;
+ size_t len;
+
+ /*
+ * Since we cannot modify the size of the output buffer we fake it
+ * by not filling it more than u->buffer_size.
+ */
+ xtime0 = pa_rtclock_usec();
+ buffered_bytes = get_playback_buffered_bytes(u);
+ if (buffered_bytes >= (uint64_t)u->buffer_size)
+ break;
+
+ len = u->buffer_size - buffered_bytes;
+ len -= len % u->frame_size;
- if (!u->memchunk.length)
- pa_sink_render(u->sink, len, &u->memchunk);
+ if (len < u->min_request)
+ break;
- pa_assert(u->memchunk.length);
+ if (u->memchunk.length < len)
+ pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk);
p = pa_memblock_acquire(u->memchunk.memblock);
- r = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL);
+ w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL);
pa_memblock_release(u->memchunk.memblock);
- if (r < 0) {
- if (errno == EINTR)
- continue;
- else if (errno != EAGAIN) {
- pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
- goto fail;
+ if (w <= 0) {
+ switch (errno) {
+ case EINTR:
+ break;
+ case EAGAIN:
+ u->buffer_size = u->buffer_size * 18 / 25;
+ u->buffer_size -= u->buffer_size % u->frame_size;
+ u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
+ pa_sink_set_max_request(u->sink, u->buffer_size);
+ pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
+ break;
+ default:
+ pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
+ goto fail;
}
} else {
- pa_assert(r % u->frame_size == 0);
+ pa_assert(w % u->frame_size == 0);
- u->memchunk.index += r;
- u->memchunk.length -= r;
+ pa_mutex_lock(u->written_bytes_lock);
+ u->written_bytes += w;
+ u->memchunk.length -= w;
+ pa_mutex_unlock(u->written_bytes_lock);
+ u->memchunk.index += w;
if (u->memchunk.length <= 0) {
pa_memblock_unref(u->memchunk.memblock);
pa_memchunk_reset(&u->memchunk);
}
-
- len -= r;
- u->written_bytes += r;
}
}
+
+ pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
+ } else {
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
}
/* Try to read some data and pass it on to the source driver */
- if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN))) {
+ if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && (revents & POLLIN)) {
pa_memchunk memchunk;
- int err;
- size_t l;
void *p;
ssize_t r;
- audio_info_t info;
+ size_t len;
err = ioctl(u->fd, AUDIO_GETINFO, &info);
pa_assert(err >= 0);
if (info.record.error) {
- pa_log_debug("Solaris buffer overflow!");
- clear_overflow(u);
+ pa_log_debug("buffer overflow!");
+
+ AUDIO_INITINFO(&info);
+ info.record.error = 0;
+ if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
}
- err = ioctl(u->fd, I_NREAD, &l);
+ err = ioctl(u->fd, I_NREAD, &len);
pa_assert(err >= 0);
- if (l > 0) {
- /* This is to make sure it fits in the memory pool. Also, a page
- should be the most efficient transfer size. */
- if (l > u->page_size)
- l = u->page_size;
-
- memchunk.memblock = pa_memblock_new(u->core->mempool, l);
+ if (len > 0) {
+ memchunk.memblock = pa_memblock_new(u->core->mempool, len);
pa_assert(memchunk.memblock);
p = pa_memblock_acquire(memchunk.memblock);
- r = pa_read(u->fd, p, l, NULL);
+ r = pa_read(u->fd, p, len, NULL);
pa_memblock_release(memchunk.memblock);
if (r < 0) {
pa_memblock_unref(memchunk.memblock);
- if (errno != EAGAIN) {
+ if (errno == EAGAIN)
+ break;
+ else {
pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
goto fail;
}
} else {
+ u->read_bytes += r;
+
memchunk.index = 0;
memchunk.length = r;
pa_source_post(u->source, &memchunk);
pa_memblock_unref(memchunk.memblock);
- u->read_bytes += r;
-
revents &= ~POLLIN;
}
}
}
- if (u->fd >= 0) {
+ if (u->rtpoll_item) {
struct pollfd *pollfd;
+ pa_assert(u->fd >= 0);
+
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- pollfd->events =
- ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0);
+ pollfd->events = (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0;
}
/* Hmm, nothing to do. Let's sleep */
- if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
if (ret == 0)
goto finish;
- if (u->fd >= 0) {
+ if (u->rtpoll_item) {
struct pollfd *pollfd;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
@@ -460,112 +780,29 @@ static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void
assert(u);
+ pa_log_debug("caught signal");
+
if (u->sink) {
- pa_sink_get_volume(u->sink);
- pa_sink_get_mute(u->sink);
+ pa_sink_get_volume(u->sink, TRUE);
+ pa_sink_get_mute(u->sink, TRUE);
}
if (u->source)
- pa_source_get_volume(u->source);
-}
-
-static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
- audio_info_t info;
-
- AUDIO_INITINFO(&info);
-
- if (mode != O_RDONLY) {
- info.play.sample_rate = ss->rate;
- info.play.channels = ss->channels;
- switch (ss->format) {
- case PA_SAMPLE_U8:
- info.play.precision = 8;
- info.play.encoding = AUDIO_ENCODING_LINEAR;
- break;
- case PA_SAMPLE_ALAW:
- info.play.precision = 8;
- info.play.encoding = AUDIO_ENCODING_ALAW;
- break;
- case PA_SAMPLE_ULAW:
- info.play.precision = 8;
- info.play.encoding = AUDIO_ENCODING_ULAW;
- break;
- case PA_SAMPLE_S16NE:
- info.play.precision = 16;
- info.play.encoding = AUDIO_ENCODING_LINEAR;
- break;
- default:
- return -1;
- }
- }
-
- if (mode != O_WRONLY) {
- info.record.sample_rate = ss->rate;
- info.record.channels = ss->channels;
- switch (ss->format) {
- case PA_SAMPLE_U8:
- info.record.precision = 8;
- info.record.encoding = AUDIO_ENCODING_LINEAR;
- break;
- case PA_SAMPLE_ALAW:
- info.record.precision = 8;
- info.record.encoding = AUDIO_ENCODING_ALAW;
- break;
- case PA_SAMPLE_ULAW:
- info.record.precision = 8;
- info.record.encoding = AUDIO_ENCODING_ULAW;
- break;
- case PA_SAMPLE_S16NE:
- info.record.precision = 16;
- info.record.encoding = AUDIO_ENCODING_LINEAR;
- break;
- default:
- return -1;
- }
- }
-
- if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
- if (errno == EINVAL)
- pa_log("AUDIO_SETINFO: Unsupported sample format.");
- else
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-static int pa_solaris_set_buffer(int fd, int buffer_size) {
- audio_info_t info;
-
- AUDIO_INITINFO(&info);
-
- info.play.buffer_size = buffer_size;
- info.record.buffer_size = buffer_size;
-
- if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
- if (errno == EINVAL)
- pa_log("AUDIO_SETINFO: Unsupported buffer size.");
- else
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
- return -1;
- }
-
- return 0;
+ pa_source_get_volume(u->source, TRUE);
}
int pa__init(pa_module *m) {
struct userdata *u = NULL;
- const char *p;
- int fd = -1;
- int buffer_size;
- int mode;
- int record = 1, playback = 1;
+ pa_bool_t record = TRUE, playback = TRUE;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
- char *t;
- struct pollfd *pollfd;
+ int fd;
+ pa_sink_new_data sink_new_data;
+ pa_source_new_data source_new_data;
+ char const *name;
+ char *name_buf;
+ pa_bool_t namereg_fail;
pa_assert(m);
@@ -575,7 +812,7 @@ int pa__init(pa_module *m) {
}
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 numeric argument.");
+ pa_log("record= and playback= expect a boolean argument.");
goto fail;
}
@@ -584,97 +821,135 @@ int pa__init(pa_module *m) {
goto fail;
}
- mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
-
- buffer_size = 16384;
- if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
- pa_log("failed to parse buffer size argument");
+ if (!(u = pa_xnew0(struct userdata, 1)))
goto fail;
- }
+
+ u->sample_counter_lock = pa_mutex_new(FALSE, FALSE);
+ u->written_bytes_lock = pa_mutex_new(FALSE, FALSE);
+
+ /*
+ * For a process (or several processes) to use the same audio device for both
+ * record and playback at the same time, the device's mixer must be enabled.
+ * See mixerctl(1). It may be turned off for playback only or record only.
+ */
+ u->mode = (playback && record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
pa_log("failed to parse sample specification");
goto fail;
}
+ u->frame_size = pa_frame_size(&ss);
- if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
+ u->buffer_size = 16384;
+ if (pa_modargs_get_value_s32(ma, "buffer_size", &u->buffer_size) < 0) {
+ pa_log("failed to parse buffer size argument");
goto fail;
-
- pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
-
- if (pa_solaris_auto_format(fd, mode, &ss) < 0)
+ }
+ u->buffer_size -= u->buffer_size % u->frame_size;
+ if (u->buffer_size < (int32_t)MIN_BUFFER_SIZE) {
+ pa_log("supplied buffer size argument is too small");
goto fail;
+ }
+
+ u->device_name = pa_xstrdup(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
- if (pa_solaris_set_buffer(fd, buffer_size) < 0)
+ if ((fd = open_audio_device(u, &ss)) < 0)
goto fail;
- u = pa_xmalloc(sizeof(struct userdata));
u->core = m->core;
-
- u->fd = fd;
-
- pa_memchunk_reset(&u->memchunk);
-
- /* We use this to get a reasonable chunk size */
- u->page_size = PA_PAGE_SIZE;
-
- u->frame_size = pa_frame_size(&ss);
- u->buffer_size = buffer_size;
-
- u->written_bytes = 0;
- u->read_bytes = 0;
-
u->module = m;
m->userdata = u;
- pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ pa_memchunk_reset(&u->memchunk);
u->rtpoll = pa_rtpoll_new();
- pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
- pa_rtpoll_set_timer_periodic(u->rtpoll, pa_bytes_to_usec(u->buffer_size / 10, &ss));
+ u->rtpoll_item = NULL;
+ build_pollfd(u);
- u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
- pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- pollfd->fd = fd;
- pollfd->events = 0;
- pollfd->revents = 0;
+ if (u->mode != O_WRONLY) {
+ name_buf = NULL;
+ namereg_fail = TRUE;
- if (mode != O_WRONLY) {
- u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map);
- pa_assert(u->source);
+ if (!(name = pa_modargs_get_value(ma, "source_name", NULL))) {
+ name = name_buf = pa_sprintf_malloc("solaris_input.%s", pa_path_get_filename(u->device_name));
+ namereg_fail = FALSE;
+ }
+
+ pa_source_new_data_init(&source_new_data);
+ source_new_data.driver = __FILE__;
+ source_new_data.module = m;
+ pa_source_new_data_set_name(&source_new_data, name);
+ source_new_data.namereg_fail = namereg_fail;
+ pa_source_new_data_set_sample_spec(&source_new_data, &ss);
+ pa_source_new_data_set_channel_map(&source_new_data, &map);
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "solaris");
+ pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Solaris PCM source");
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "serial");
+ pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) u->buffer_size);
+
+ u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL);
+ pa_source_new_data_done(&source_new_data);
+ pa_xfree(name_buf);
+
+ if (!u->source) {
+ pa_log("Failed to create source object");
+ goto fail;
+ }
u->source->userdata = u;
u->source->parent.process_msg = source_process_msg;
- pa_source_set_module(u->source, m);
- pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
- pa_xfree(t);
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
- u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL;
- u->source->refresh_volume = 1;
+ u->source->get_volume = source_get_volume;
+ u->source->set_volume = source_set_volume;
+ u->source->refresh_volume = TRUE;
} else
u->source = NULL;
- if (mode != O_RDONLY) {
- u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map);
- pa_assert(u->sink);
+ if (u->mode != O_RDONLY) {
+ name_buf = NULL;
+ namereg_fail = TRUE;
+ if (!(name = pa_modargs_get_value(ma, "sink_name", NULL))) {
+ name = name_buf = pa_sprintf_malloc("solaris_output.%s", pa_path_get_filename(u->device_name));
+ namereg_fail = FALSE;
+ }
+
+ pa_sink_new_data_init(&sink_new_data);
+ sink_new_data.driver = __FILE__;
+ sink_new_data.module = m;
+ pa_sink_new_data_set_name(&sink_new_data, name);
+ sink_new_data.namereg_fail = namereg_fail;
+ pa_sink_new_data_set_sample_spec(&sink_new_data, &ss);
+ pa_sink_new_data_set_channel_map(&sink_new_data, &map);
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "solaris");
+ pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Solaris PCM sink");
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "serial");
+
+ u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL|PA_SINK_HW_MUTE_CTRL);
+ pa_sink_new_data_done(&sink_new_data);
+ pa_assert(u->sink);
u->sink->userdata = u;
u->sink->parent.process_msg = sink_process_msg;
- pa_sink_set_module(u->sink, m);
- pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
- pa_xfree(t);
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
- u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL;
- u->sink->refresh_volume = 1;
- u->sink->refresh_mute = 1;
+ u->sink->get_volume = sink_get_volume;
+ u->sink->set_volume = sink_set_volume;
+ u->sink->get_mute = sink_get_mute;
+ u->sink->set_mute = sink_set_mute;
+ u->sink->refresh_volume = u->sink->refresh_muted = TRUE;
+
+ u->sink->thread_info.max_request = u->buffer_size;
+ u->min_request = pa_usec_to_bytes(PA_USEC_PER_SEC / MAX_RENDER_HZ, &ss);
} else
u->sink = NULL;
@@ -690,17 +965,28 @@ int pa__init(pa_module *m) {
}
/* Read mixer settings */
- if (u->source)
- pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_GET_VOLUME, &u->source->volume, 0, NULL);
if (u->sink) {
- pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_VOLUME, &u->sink->volume, 0, NULL);
- pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_MUTE, &u->sink->muted, 0, NULL);
- }
+ if (sink_new_data.volume_is_set)
+ u->sink->set_volume(u->sink);
+ else
+ u->sink->get_volume(u->sink);
+
+ if (sink_new_data.muted_is_set)
+ u->sink->set_mute(u->sink);
+ else
+ u->sink->get_mute(u->sink);
- if (u->sink)
pa_sink_put(u->sink);
- if (u->source)
+ }
+
+ if (u->source) {
+ if (source_new_data.volume_is_set)
+ u->source->set_volume(u->source);
+ else
+ u->source->get_volume(u->source);
+
pa_source_put(u->source);
+ }
pa_modargs_free(ma);
@@ -748,7 +1034,7 @@ void pa__done(pa_module *m) {
if (u->source)
pa_source_unref(u->source);
- if (u->memchunk.memblock)
+ if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
if (u->rtpoll_item)
@@ -760,5 +1046,10 @@ void pa__done(pa_module *m) {
if (u->fd >= 0)
close(u->fd);
+ pa_mutex_free(u->written_bytes_lock);
+ pa_mutex_free(u->sample_counter_lock);
+
+ pa_xfree(u->device_name);
+
pa_xfree(u);
}
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index 8e59a577..b3f243c3 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -51,7 +51,12 @@
#include <pulsecore/macro.h>
#include <pulsecore/strbuf.h>
#include <pulsecore/random.h>
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else
#include <pulsecore/poll.h>
+#endif
#include "raop_client.h"
#include "rtsp_client.h"
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index b0b8a1e0..98db05dd 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -43,9 +43,14 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/strbuf.h>
-#include <pulsecore/poll.h>
#include <pulsecore/ioline.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else
+#include <pulsecore/poll.h>
+#endif
+
#include "rtsp_client.h"
struct pa_rtsp_client {
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index a184bebd..0d243ee6 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2505,7 +2505,7 @@ char *pa_machine_id(void) {
char *pa_uname_string(void) {
struct utsname u;
- pa_assert_se(uname(&u) == 0);
+ pa_assert_se(uname(&u) >= 0);
return pa_sprintf_malloc("%s %s %s %s", u.sysname, u.machine, u.release, u.version);
}
diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c
index dcbd1184..56ab2ef0 100644
--- a/src/pulsecore/rtclock.c
+++ b/src/pulsecore/rtclock.c
@@ -27,9 +27,12 @@
#include <stddef.h>
#include <time.h>
#include <sys/time.h>
-#include <sys/prctl.h>
#include <errno.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
#include <pulse/timeval.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-error.h>