]> code.delx.au - pulseaudio/blobdiff - src/modules/module-solaris.c
sink, source: Assign to s->muted from only one place
[pulseaudio] / src / modules / module-solaris.c
index ecd3ba323e5143ea6afd4eb3d45c4c5ce9aeba6d..71a98e984f37161c7a89f169f1d8002b4268bcd1 100644 (file)
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
-#include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 #include <sys/ioctl.h>
-#include <sys/stat.h>
 #include <sys/types.h>
 
 #include <signal.h>
 #include <sys/conf.h>
 #include <sys/audio.h>
 
-#include <pulse/error.h>
 #include <pulse/mainloop-signal.h>
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 #include <pulse/util.h>
+#include <pulse/rtclock.h>
 
-#include <pulsecore/iochannel.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/source.h>
 #include <pulsecore/module.h>
@@ -59,7 +55,7 @@
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/thread.h>
-#include <pulsecore/rtclock.h>
+#include <pulsecore/time-smoother.h>
 
 #include "module-solaris-symdef.h"
 
@@ -68,7 +64,9 @@ PA_MODULE_DESCRIPTION("Solaris Sink/Source");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_USAGE(
     "sink_name=<name for the sink> "
+    "sink_properties=<properties for the sink> "
     "source_name=<name for the source> "
+    "source_properties=<properties for the source> "
     "device=<audio device file name> "
     "record=<enable source?> "
     "playback=<enable sink?> "
@@ -77,7 +75,7 @@ PA_MODULE_USAGE(
     "rate=<sample rate> "
     "buffer_length=<milliseconds> "
     "channel_map=<channel map>");
-PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_LOAD_ONCE(false);
 
 struct userdata {
     pa_core *core;
@@ -102,17 +100,21 @@ struct userdata {
     pa_rtpoll_item *rtpoll_item;
     pa_module *module;
 
-    pa_bool_t sink_suspended, source_suspended;
+    bool sink_suspended, source_suspended;
 
     uint32_t play_samples_msw, record_samples_msw;
     uint32_t prev_playback_samples, prev_record_samples;
 
     int32_t minimum_request;
+
+    pa_smoother *smoother;
 };
 
 static const char* const valid_modargs[] = {
     "sink_name",
+    "sink_properties",
     "source_name",
+    "source_properties",
     "device",
     "record",
     "playback",
@@ -129,6 +131,9 @@ static const char* const valid_modargs[] = {
 #define MAX_RENDER_HZ   (300)
 /* This render rate limit imposes a minimum latency, but without it we waste too much CPU time. */
 
+#define MAX_BUFFER_SIZE (128 * 1024)
+/* An attempt to buffer more than 128 KB causes write() to fail with errno == EAGAIN. */
+
 static uint64_t get_playback_buffered_bytes(struct userdata *u) {
     audio_info_t info;
     uint64_t played_bytes;
@@ -141,7 +146,12 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {
 
     /* 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! */
+        /*
+         * Unfortunately info.play.samples can sometimes go backwards, even before it wraps!
+         * The bug seems to be absent on Solaris x86 nv117 with audio810 driver, at least on this (UP) machine.
+         * The bug is present on a different (SMP) machine running Solaris x86 nv103 with audioens driver.
+         * An earlier revision of this file mentions the same bug independently (unknown configuration).
+         */
         if (u->prev_playback_samples + info.play.samples < 240000) {
             ++u->play_samples_msw;
         } else {
@@ -151,7 +161,12 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {
     u->prev_playback_samples = info.play.samples;
     played_bytes = (((uint64_t)u->play_samples_msw << 32) + info.play.samples) * u->frame_size;
 
-    return u->written_bytes - played_bytes;
+    pa_smoother_put(u->smoother, pa_rtclock_now(), pa_bytes_to_usec(played_bytes, &u->sink->sample_spec));
+
+    if (u->written_bytes > played_bytes)
+        return u->written_bytes - played_bytes;
+    else
+        return 0;
 }
 
 static pa_usec_t sink_get_latency(struct userdata *u, pa_sample_spec *ss) {
@@ -290,8 +305,8 @@ static int auto_format(int fd, int mode, pa_sample_spec *ss) {
             info.record.encoding = AUDIO_ENCODING_LINEAR;
             break;
         default:
-             pa_log("AUDIO_SETINFO: Unsupported sample format.");
-             return -1;
+            pa_log("AUDIO_SETINFO: Unsupported sample format.");
+            return -1;
         }
     }
 
@@ -310,7 +325,7 @@ 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) {
+    if ((u->fd = pa_open_cloexec(u->device_name, u->mode | O_NONBLOCK, 0)) < 0) {
         pa_log_warn("open %s failed (%s)", u->device_name, pa_cstrerror(errno));
         return -1;
     }
@@ -336,7 +351,7 @@ static int suspend(struct userdata *u) {
 
     pa_log_info("Suspending...");
 
-    ioctl(u->fd, AUDIO_DRAIN, NULL);
+    ioctl(u->fd, I_FLUSH, FLUSHRW);
     pa_close(u->fd);
     u->fd = -1;
 
@@ -383,24 +398,28 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
                     pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 
+                    pa_smoother_pause(u->smoother, pa_rtclock_now());
+
                     if (!u->source || u->source_suspended) {
                         if (suspend(u) < 0)
                             return -1;
                     }
-                    u->sink_suspended = TRUE;
+                    u->sink_suspended = true;
                     break;
 
                 case PA_SINK_IDLE:
                 case PA_SINK_RUNNING:
 
                     if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+                        pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
+
                         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;
+                        u->sink_suspended = false;
                     }
                     break;
 
@@ -437,7 +456,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
                         if (suspend(u) < 0)
                             return -1;
                     }
-                    u->source_suspended = TRUE;
+                    u->source_suspended = true;
                     break;
 
                 case PA_SOURCE_IDLE:
@@ -449,7 +468,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
                                 return -1;
                             u->source->get_volume(u->source);
                         }
-                        u->source_suspended = FALSE;
+                        u->source_suspended = false;
                     }
                     break;
 
@@ -475,7 +494,7 @@ static void sink_set_volume(pa_sink *s) {
     if (u->fd >= 0) {
         AUDIO_INITINFO(&info);
 
-        info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+        info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
         assert(info.play.gain <= AUDIO_MAX_GAIN);
 
         if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
@@ -497,8 +516,7 @@ static void sink_get_volume(pa_sink *s) {
         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);
+            pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
     }
 }
 
@@ -511,7 +529,7 @@ static void source_set_volume(pa_source *s) {
     if (u->fd >= 0) {
         AUDIO_INITINFO(&info);
 
-        info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+        info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
         assert(info.play.gain <= AUDIO_MAX_GAIN);
 
         if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
@@ -533,8 +551,7 @@ static void source_get_volume(pa_source *s) {
         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);
+            pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
     }
 }
 
@@ -554,18 +571,23 @@ static void sink_set_mute(pa_sink *s) {
     }
 }
 
-static void sink_get_mute(pa_sink *s) {
+static int sink_get_mute(pa_sink *s, bool *mute) {
     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;
+    if (u->fd < 0)
+        return -1;
+
+    if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0) {
+        pa_log("AUDIO_GETINFO: %s", pa_cstrerror(errno));
+        return -1;
     }
+
+    *mute = info.output_muted;
+
+    return 0;
 }
 
 static void process_rewind(struct userdata *u) {
@@ -573,14 +595,21 @@ static void process_rewind(struct userdata *u) {
 
     pa_assert(u);
 
-    /* Figure out how much we shall rewind and reset the counter */
+    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+        pa_sink_process_rewind(u->sink, 0);
+        return;
+    }
+
     rewind_nbytes = u->sink->thread_info.rewind_nbytes;
-    u->sink->thread_info.rewind_nbytes = 0;
 
     if (rewind_nbytes > 0) {
         pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
         rewind_nbytes = PA_MIN(u->memchunk.length, rewind_nbytes);
         u->memchunk.length -= rewind_nbytes;
+        if (u->memchunk.length <= 0 && u->memchunk.memblock) {
+            pa_memblock_unref(u->memchunk.memblock);
+            pa_memchunk_reset(&u->memchunk);
+        }
         pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
     }
 
@@ -601,18 +630,19 @@ static void thread_func(void *userdata) {
         pa_make_realtime(u->core->realtime_priority);
 
     pa_thread_mq_install(&u->thread_mq);
-    pa_rtpoll_install(u->rtpoll);
+
+    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
 
     for (;;) {
         /* Render some data and write it to the dsp */
 
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            process_rewind(u);
+
         if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
-            pa_usec_t xtime0;
+            pa_usec_t xtime0, ysleep_interval, xsleep_interval;
             uint64_t buffered_bytes;
 
-            if (u->sink->thread_info.rewind_requested)
-                process_rewind(u);
-
             err = ioctl(u->fd, AUDIO_GETINFO, &info);
             if (err < 0) {
                 pa_log("AUDIO_GETINFO ioctl failed: %s", pa_cstrerror(errno));
@@ -626,18 +656,21 @@ static void thread_func(void *userdata) {
                 info.play.error = 0;
                 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
                     pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+
+                pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
             }
 
             for (;;) {
                 void *p;
                 ssize_t w;
                 size_t len;
+                int write_type = 1;
 
                 /*
                  * 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();
+                xtime0 = pa_rtclock_now();
                 buffered_bytes = get_playback_buffered_bytes(u);
                 if (buffered_bytes >= (uint64_t)u->buffer_size)
                     break;
@@ -648,39 +681,32 @@ static void thread_func(void *userdata) {
                 if (len < (size_t) u->minimum_request)
                     break;
 
-                if (u->memchunk.length < len)
+                if (!u->memchunk.length)
                     pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk);
 
+                len = PA_MIN(u->memchunk.length, len);
+
                 p = pa_memblock_acquire(u->memchunk.memblock);
-                w = 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, len, &write_type);
                 pa_memblock_release(u->memchunk.memblock);
 
                 if (w <= 0) {
-                    switch (errno) {
-                        case EINTR:
-                            continue;
-                        case EAGAIN:
-                            /* If the buffer_size is too big, we get EAGAIN. Avoiding that limit by trial and error
-                             * is not ideal, but I don't know how to get the system to tell me what the limit is.
-                             */
-                            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, 2 * u->minimum_request);
-                            pa_sink_set_max_request_within_thread(u->sink, u->buffer_size);
-                            pa_sink_set_max_rewind_within_thread(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;
+                    if (errno == EINTR) {
+                        continue;
+                    } else if (errno == EAGAIN) {
+                        /* We may have realtime priority so yield the CPU to ensure that fd can become writable again. */
+                        pa_log_debug("EAGAIN with %llu bytes buffered.", buffered_bytes);
+                        break;
+                    } else {
+                        pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
+                        goto fail;
                     }
                 } else {
                     pa_assert(w % u->frame_size == 0);
 
                     u->written_bytes += w;
-                    u->memchunk.length -= w;
-
                     u->memchunk.index += w;
+                    u->memchunk.length -= w;
                     if (u->memchunk.length <= 0) {
                         pa_memblock_unref(u->memchunk.memblock);
                         pa_memchunk_reset(&u->memchunk);
@@ -688,7 +714,9 @@ static void thread_func(void *userdata) {
                 }
             }
 
-            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
+            ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec);
+            xsleep_interval = pa_smoother_translate(u->smoother, xtime0, ysleep_interval);
+            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + PA_MIN(xsleep_interval, ysleep_interval));
         } else
             pa_rtpoll_set_timer_disabled(u->rtpoll);
 
@@ -755,7 +783,7 @@ static void thread_func(void *userdata) {
         }
 
         /* Hmm, nothing to do. Let's sleep */
-        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+        if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
             goto fail;
 
         if (ret == 0)
@@ -794,27 +822,27 @@ static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void
     pa_log_debug("caught signal");
 
     if (u->sink) {
-        pa_sink_get_volume(u->sink, TRUE);
-        pa_sink_get_mute(u->sink, TRUE);
+        pa_sink_get_volume(u->sink, true);
+        pa_sink_get_mute(u->sink, true);
     }
 
     if (u->source)
-        pa_source_get_volume(u->source, TRUE);
+        pa_source_get_volume(u->source, true);
 }
 
 int pa__init(pa_module *m) {
     struct userdata *u = NULL;
-    pa_bool_t record = TRUE, playback = TRUE;
+    bool record = true, playback = true;
     pa_sample_spec ss;
     pa_channel_map map;
     pa_modargs *ma = NULL;
     uint32_t buffer_length_msec;
-    int fd;
+    int fd = -1;
     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;
+    bool namereg_fail;
 
     pa_assert(m);
 
@@ -835,6 +863,9 @@ int pa__init(pa_module *m) {
 
     u = pa_xnew0(struct userdata, 1);
 
+    if (!(u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC * 2, true, true, 10, pa_rtclock_now(), true)))
+        goto fail;
+
     /*
      * 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.
@@ -858,7 +889,13 @@ int pa__init(pa_module *m) {
     }
     u->buffer_size = pa_usec_to_bytes(1000 * buffer_length_msec, &ss);
     if (u->buffer_size < 2 * u->minimum_request) {
-        pa_log("supplied buffer size argument is too small");
+        pa_log("buffer_length argument cannot be smaller than %u",
+               (unsigned)(pa_bytes_to_usec(2 * u->minimum_request, &ss) / 1000));
+        goto fail;
+    }
+    if (u->buffer_size > MAX_BUFFER_SIZE) {
+        pa_log("buffer_length argument cannot be greater than %u",
+               (unsigned)(pa_bytes_to_usec(MAX_BUFFER_SIZE, &ss) / 1000));
         goto fail;
     }
 
@@ -881,11 +918,11 @@ int pa__init(pa_module *m) {
 
     if (u->mode != O_WRONLY) {
         name_buf = NULL;
-        namereg_fail = TRUE;
+        namereg_fail = true;
 
         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;
+            namereg_fail = false;
         }
 
         pa_source_new_data_init(&source_new_data);
@@ -897,11 +934,17 @@ int pa__init(pa_module *m) {
         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_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);
+        if (pa_modargs_get_proplist(ma, "source_properties", source_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
+            pa_log("Invalid properties");
+            pa_source_new_data_done(&source_new_data);
+            goto fail;
+        }
+
+        u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
         pa_source_new_data_done(&source_new_data);
         pa_xfree(name_buf);
 
@@ -915,19 +958,20 @@ int pa__init(pa_module *m) {
 
         pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
         pa_source_set_rtpoll(u->source, u->rtpoll);
+        pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->source->sample_spec));
 
-        u->source->get_volume = source_get_volume;
-        u->source->set_volume = source_set_volume;
-        u->source->refresh_volume = TRUE;
+        pa_source_set_get_volume_callback(u->source, source_get_volume);
+        pa_source_set_set_volume_callback(u->source, source_set_volume);
+        u->source->refresh_volume = true;
     } else
         u->source = NULL;
 
     if (u->mode != O_RDONLY) {
         name_buf = NULL;
-        namereg_fail = TRUE;
+        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;
+            namereg_fail = false;
         }
 
         pa_sink_new_data_init(&sink_new_data);
@@ -939,10 +983,16 @@ int pa__init(pa_module *m) {
         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_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);
+        if (pa_modargs_get_proplist(ma, "sink_properties", sink_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
+            pa_log("Invalid properties");
+            pa_sink_new_data_done(&sink_new_data);
+            goto fail;
+        }
+
+        u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
         pa_sink_new_data_done(&sink_new_data);
 
         pa_assert(u->sink);
@@ -951,15 +1001,15 @@ int pa__init(pa_module *m) {
 
         pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
         pa_sink_set_rtpoll(u->sink, u->rtpoll);
-
-        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;
-
+        pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->buffer_size, &u->sink->sample_spec));
         pa_sink_set_max_request(u->sink, u->buffer_size);
         pa_sink_set_max_rewind(u->sink, u->buffer_size);
+
+        pa_sink_set_get_volume_callback(u->sink, sink_get_volume);
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
+        pa_sink_set_get_mute_callback(u->sink, sink_get_mute);
+        pa_sink_set_set_mute_callback(u->sink, sink_set_mute);
+        u->sink->refresh_volume = u->sink->refresh_muted = true;
     } else
         u->sink = NULL;
 
@@ -971,7 +1021,7 @@ int pa__init(pa_module *m) {
     else
         pa_log_warn("Could not register SIGPOLL handler");
 
-    if (!(u->thread = pa_thread_new(thread_func, u))) {
+    if (!(u->thread = pa_thread_new("solaris", thread_func, u))) {
         pa_log("Failed to create thread.");
         goto fail;
     }
@@ -1060,6 +1110,9 @@ void pa__done(pa_module *m) {
     if (u->fd >= 0)
         close(u->fd);
 
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
     pa_xfree(u->device_name);
 
     pa_xfree(u);