[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master, updated. v0.9.15-test3-34-gf3de61e

Lennart Poettering gitmailer-noreply at 0pointer.de
Tue Mar 3 13:28:30 PST 2009


This is an automated email from the git hooks/post-receive script. It was
generated because of a push to the "PulseAudio Sound Server" repository.

The master branch has been updated
      from  ff38eaf67773e0039befb95c0f9ad91e7a06fc3f (commit)

- Log -----------------------------------------------------------------
f3de61e... fix device reservation for system mode
0329edd... revive solaris module
-----------------------------------------------------------------------

Summary of changes:
 bootstrap.sh                        |    2 +-
 configure.ac                        |   16 +-
 src/Makefile.am                     |   20 +-
 src/daemon/daemon-conf.c            |   10 +-
 src/daemon/daemon-conf.h            |    6 +-
 src/daemon/main.c                   |    2 +
 src/modules/alsa/alsa-sink.c        |    3 +
 src/modules/alsa/alsa-source.c      |    3 +
 src/modules/alsa/module-alsa-card.c |    5 +-
 src/modules/module-solaris.c        |  957 +++++++++++++++++++++++------------
 src/modules/raop/raop_client.c      |    5 +
 src/modules/rtp/rtsp_client.c       |    7 +-
 src/pulsecore/core-util.c           |    2 +-
 src/pulsecore/rtclock.c             |    5 +-
 14 files changed, 692 insertions(+), 351 deletions(-)

-----------------------------------------------------------------------

commit 0329edd1791e3c8fbed33f266d86cae6b91a5556
Author: Finn Thain <fthain at telegraphics.com.au>
Date:   Thu Feb 26 16:48:58 2009 +1100

    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

diff --git a/bootstrap.sh b/bootstrap.sh
index 4e223e3..cb74121 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -62,7 +62,7 @@ else
 
     rm -f Makefile.am~ configure.ac~
     # Evil, evil, evil, evil hack
-    sed 's/read dummy/\#/' `which gettextize` | sh -s -- --copy --force
+    sed 's/read dummy/\#/' `which gettextize` | bash -s -- --copy --force
     test -f Makefile.am~ && mv Makefile.am~ Makefile.am
     test -f configure.ac~ && mv configure.ac~ configure.ac
 
diff --git a/configure.ac b/configure.ac
index 77d777d..049589d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -70,8 +70,7 @@ fi
 
 case $host in
    *-*-solaris* )
-      AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, Needed to get declarations for msg_control and msg_controllen on Solaris)
-      AC_DEFINE(_XOPEN_SOURCE,          2, Needed to get declarations for msg_control and msg_controllen on Solaris)
+      AC_DEFINE(_XOPEN_SOURCE,        600, Needed to get declarations for msg_control and msg_controllen on Solaris)
       AC_DEFINE(__EXTENSIONS__,         1, Needed to get declarations for msg_control and msg_controllen on Solaris)
       ;;
 esac
@@ -92,11 +91,20 @@ AC_USE_SYSTEM_EXTENSIONS
 
 # M4
 
-AC_PATH_PROG([M4], [m4 gm4], [no])
+AC_CHECK_PROGS([M4], gm4 m4, no)
 if test "x$M4" = xno ; then
    AC_MSG_ERROR([m4 missing])
 fi
 
+# Linker
+
+AC_PROG_LD
+AC_PROG_LD_GNU
+
+AC_SUBST(HAVE_GNU_LD)
+AM_CONDITIONAL([HAVE_GNU_LD], [test "x$HAVE_GNU_LD" = x1])
+
+
 dnl Compiler flags
 DESIRED_FLAGS="-Wall -W -Wextra -pipe -Wno-long-long -Winline -Wvla -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -ffast-math -Wp,-D_FORTIFY_SOURCE=2 -fno-common -fdiagnostics-show-option"
 
@@ -1059,7 +1067,7 @@ if test "x$HAVE_HAL" = x1 ; then
    dbus=yes
 fi
 
-if test "x${dbus}" != xno || test "x${bluez}" != xno || "x${hal}" != xno ; then
+if test "x${dbus}" != xno || test "x${bluez}" != xno || test "x${hal}" != xno ; then
 
     PKG_CHECK_MODULES(DBUS, [ dbus-1 >= 1.0.0 ],
         [
diff --git a/src/Makefile.am b/src/Makefile.am
index 9f2fa02..aa190a4 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- at 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- at 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- at 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- at 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 b02377a..ac6cc8a 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 787676c..9cec189 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 46a279d..0048e7b 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 738115c..783c244 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 at 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 8e59a57..b3f243c 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 b0b8a1e..98db05d 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 a184beb..0d243ee 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 dcbd118..56ab2ef 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>

commit f3de61edf68c039abf3499463333ad920019f430
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Mar 3 22:26:15 2009 +0100

    fix device reservation for system mode

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index c8e12f9..bfe4f3b 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -164,6 +164,9 @@ static int reserve_init(struct userdata *u, const char *dname) {
     if (u->reserve)
         return 0;
 
+    if (pa_in_system_mode())
+        return 0;
+
     /* We are resuming, try to lock the device */
     if (!(rname = pa_alsa_get_reserve_name(dname)))
         return 0;
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index ed9388e..04cb262 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -162,6 +162,9 @@ static int reserve_init(struct userdata *u, const char *dname) {
     if (u->reserve)
         return 0;
 
+    if (pa_in_system_mode())
+        return 0;
+
     /* We are resuming, try to lock the device */
     if (!(rname = pa_alsa_get_reserve_name(dname)))
         return 0;
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 22f0ae0..d5e2cdc 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -310,8 +310,9 @@ int pa__init(pa_module *m) {
 
     pa_snprintf(rname, sizeof(rname), "Audio%i", alsa_card_index);
 
-    if (!(reserve = pa_reserve_wrapper_get(m->core, rname)))
-        goto fail;
+    if (!pa_in_system_mode())
+        if (!(reserve = pa_reserve_wrapper_get(m->core, rname)))
+            goto fail;
 
     pa_card_new_data_init(&data);
     data.driver = __FILE__;

-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list