move repository to Debian's GitLab
[osspd.git] / ossp-padsp.c
diff --git a/ossp-padsp.c b/ossp-padsp.c
deleted file mode 100644 (file)
index 1871f5b..0000000
+++ /dev/null
@@ -1,1524 +0,0 @@
-/*
- * ossp-padsp - ossp DSP slave which forwards to pulseaduio
- *
- * Copyright (C) 2008-2010  SUSE Linux Products GmbH
- * Copyright (C) 2008-2010  Tejun Heo <tj@kernel.org>
- *
- * This file is released under the GPLv2.
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <libgen.h>
-#include <limits.h>
-#include <poll.h>
-#include <pthread.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <pulse/pulseaudio.h>
-#include <sys/soundcard.h>
-
-#include "ossp-slave.h"
-
-enum {
-       AFMT_FLOAT              = 0x00004000,
-       AFMT_S32_LE             = 0x00001000,
-       AFMT_S32_BE             = 0x00002000,
-};
-
-/* everything is in millisecs */
-struct stream_params {
-       size_t          min_process;
-       size_t          min_latency;
-       size_t          dfl_process;
-       size_t          dfl_latency;
-       size_t          mmap_process;
-       size_t          mmap_latency;
-       size_t          mmap_lead;
-       size_t          mmap_staging;
-};
-
-/* TODO: make this configurable */
-static struct stream_params stream_params[] = {
-       [ PLAY ] = { .min_process = 25,         .min_latency = 100,
-                    .dfl_process = 50,         .dfl_latency = 200,
-                    .mmap_process = 25,        .mmap_latency = 50,
-                    .mmap_lead = 25,           .mmap_staging = 100 },
-       [ REC ]  = { .min_process = 25,         .min_latency = 200,
-                    .dfl_process = 50,         .dfl_latency = 400,
-                    .mmap_process = 25,        .mmap_latency = 50,
-                    .mmap_lead = 25,           .mmap_staging = 1000 },
-};
-
-static size_t page_size;
-static pa_context *context;
-static pa_threaded_mainloop *mainloop;
-static pa_mainloop_api *mainloop_api;
-static char stream_name[128];
-static int stream_enabled[2];
-static int stream_corked[2];
-static int stream_waiting;
-static int stream_notify;
-static pa_channel_map channel_map_stor;
-static pa_channel_map *channel_map;
-static pa_stream *stream[2];
-static pa_usec_t stream_ptr_timestamp[2];
-static struct ring_buf rec_buf;
-static int stored_oss_vol[2][2] = { { -1, -1 }, { -1, -1 } };
-static int fail_code;
-
-static pa_sample_spec sample_spec = {
-       .format = PA_SAMPLE_U8,
-       .rate = 8000,
-       .channels = 1,
-};
-static size_t sample_bps = 8000;
-static size_t frame_size = 1;
-
-/* user visible stream parameters */
-static size_t user_frag_size;
-static size_t user_subdivision;        /* alternative way to determine frag_size */
-static size_t user_max_frags;  /* maximum number of fragments */
-static size_t user_max_length;
-
-/* actual stream parameters */
-static size_t frag_size;
-static size_t target_length;
-static size_t max_length;
-static size_t prebuf_size;
-
-/* mmap stuff */
-static size_t mmap_raw_size, mmap_size;
-static void *mmap_map[2];
-static uint64_t mmap_idx[2];           /* mmap pointer */
-static uint64_t mmap_last_idx[2];      /* last idx for get_ptr */
-static struct ring_buf mmap_stg[2];    /* staging ring buffer */
-static size_t mmap_lead[2];            /* lead bytes */
-static int mmap_sync[2];               /* sync with backend stream */
-
-static const char *dir_str[] = {
-       [PLAY]          = "PLAY",
-       [REC]           = "REC",
-};
-
-static void stream_rw_callback(pa_stream *s, size_t length, void *userdata);
-
-#define __pa_err               pa_strerror(pa_context_errno(context))
-#define dbg1_pa(fmt, args...)  dbg1(fmt" (%s)" , ##args, __pa_err)
-#define dbg0_pa(fmt, args...)  dbg0(fmt" (%s)" , ##args, __pa_err)
-#define info_pa(fmt, args...)  info(fmt" (%s)" , ##args, __pa_err)
-#define warn_pa(fmt, args...)  warn(fmt" (%s)" , ##args, __pa_err)
-#define err_pa(fmt, args...)   err(fmt" (%s)" , ##args, __pa_err)
-
-#define round_down(v, t)       ((v) / (t) * (t))
-#define round_up(v, t)         (((v) + (t) - 1) / (t) * (t))
-#define is_power2(v)           !((v) & ((v) - 1))
-
-static int do_mixer(int dir, int *vol);
-
-static int padsp_done(void)
-{
-       fail_code = -EIO;
-       mainloop_api->quit(mainloop_api, 1);
-       return fail_code;
-}
-
-static int fmt_oss_to_pa(int fmt)
-{
-       switch (fmt) {
-       case AFMT_U8:                   return PA_SAMPLE_U8;
-       case AFMT_A_LAW:                return PA_SAMPLE_ALAW;
-       case AFMT_MU_LAW:               return PA_SAMPLE_ULAW;
-       case AFMT_S16_LE:               return PA_SAMPLE_S16LE;
-       case AFMT_S16_BE:               return PA_SAMPLE_S16BE;
-       case AFMT_FLOAT:                return PA_SAMPLE_FLOAT32NE;
-       case AFMT_S32_LE:               return PA_SAMPLE_S32LE;
-       case AFMT_S32_BE:               return PA_SAMPLE_S32BE;
-       default:                        return PA_SAMPLE_U8;
-       }
-}
-
-static int fmt_pa_to_oss(int fmt)
-{
-       switch (fmt) {
-       case PA_SAMPLE_U8:              return AFMT_U8;
-       case PA_SAMPLE_ALAW:            return AFMT_A_LAW;
-       case PA_SAMPLE_ULAW:            return AFMT_MU_LAW;
-       case PA_SAMPLE_S16LE:           return AFMT_S16_LE;
-       case PA_SAMPLE_S16BE:           return AFMT_S16_BE;
-       case PA_SAMPLE_FLOAT32NE:       return AFMT_FLOAT;
-       case PA_SAMPLE_S32LE:           return AFMT_S32_LE;
-       case PA_SAMPLE_S32BE:           return AFMT_S32_BE;
-       default:                        return AFMT_U8;
-       }
-}
-
-#define EXEC_OP(op, args...)   do {                                    \
-       pa_operation *_o;                                               \
-       _o = op(args);                                                  \
-       if (_o) {                                                       \
-               while (pa_operation_get_state(_o) != PA_OPERATION_DONE) \
-                       pa_threaded_mainloop_wait(mainloop);            \
-               pa_operation_unref(_o);                                 \
-       } } while (0)
-
-static void context_op_callback(pa_context *s, int success, void *userdata)
-{
-       *(int *)userdata = success;
-       pa_threaded_mainloop_signal(mainloop, 0);
-}
-
-static void stream_op_callback(pa_stream *s, int success, void *userdata)
-{
-       *(int *)userdata = success;
-       pa_threaded_mainloop_signal(mainloop, 0);
-}
-
-#define EXEC_CONTEXT_OP(op, args...) ({                                        \
-       int _success;                                                   \
-       EXEC_OP(op , ##args, context_op_callback, &_success);           \
-       if (!_success)                                                  \
-               warn_pa("%s() failed", #op);                            \
-       _success ? 0 : -EIO; })
-
-#define EXEC_STREAM_OP(op, args...) ({                                 \
-       int _success;                                                   \
-       EXEC_OP(op , ##args, stream_op_callback, &_success);            \
-       if (!_success)                                                  \
-               warn_pa("%s() failed", #op);                            \
-       _success ? 0 : -EIO; })
-
-static int mmapped(void)
-{
-       return mmap_map[PLAY] || mmap_map[REC];
-}
-
-static uint64_t get_mmap_idx(int dir)
-{
-       uint64_t idx;
-       pa_usec_t time;
-
-       if (!stream[dir])
-               return mmap_idx[dir];
-
-       if (pa_stream_get_time(stream[dir], &time) < 0) {
-               dbg1_pa("pa_stream_get_time() failed");
-               return mmap_idx[dir];
-       }
-
-       /* calculate the current index from time elapsed */
-       idx = ((uint64_t)time * sample_bps / 1000000);
-       /* round down to the nearest frame boundary */
-       idx = idx / frame_size * frame_size;
-
-       return idx;
-}
-
-static void flush_streams(int drain)
-{
-       int i;
-
-       if (!(stream[PLAY] || stream[REC]))
-               return;
-
-       dbg0("FLUSH drain=%d", drain);
-
-       /* mmapped streams run forever, can't drain */
-       if (drain && !mmapped() && stream[PLAY])
-               EXEC_STREAM_OP(pa_stream_drain, stream[PLAY]);
-
-       for (i = 0; i < 2; i++)
-               if (stream[i])
-                       EXEC_STREAM_OP(pa_stream_flush, stream[i]);
-
-       ring_consume(&rec_buf, ring_bytes(&rec_buf));
-}
-
-static void kill_streams(void)
-{
-       int dir;
-
-       if (!(stream[PLAY] || stream[REC]))
-               return;
-
-       flush_streams(1);
-
-       dbg0("KILL");
-
-       for (dir = 0; dir < 2; dir++) {
-               if (!stream[dir])
-                       continue;
-               pa_stream_disconnect(stream[dir]);
-               pa_stream_unref(stream[dir]);
-               stream[dir] = NULL;
-               stream_ptr_timestamp[dir] = 0;
-
-               ring_consume(&mmap_stg[dir], ring_bytes(&mmap_stg[dir]));
-               ring_resize(&mmap_stg[dir], 0);
-       }
-}
-
-static int trigger_streams(int play, int rec)
-{
-       int ret = 0, dir, rc;
-
-       if (play >= 0)
-               stream_corked[PLAY] = !play;
-       if (rec >= 0)
-               stream_corked[REC] = !rec;
-
-       for (dir = 0; dir < 2; dir++) {
-               if (!stream[dir])
-                       continue;
-
-               rc = EXEC_STREAM_OP(pa_stream_cork, stream[dir],
-                                   stream_corked[dir]);
-               if (!rc && dir == PLAY && !mmap_map[dir] && !stream_corked[dir])
-                       rc = EXEC_STREAM_OP(pa_stream_trigger, stream[dir]);
-               if (!ret)
-                       ret = rc;
-       }
-
-       return ret;
-}
-
-static void stream_state_callback(pa_stream *s, void *userdata)
-{
-       pa_threaded_mainloop_signal(mainloop, 0);
-}
-
-static void stream_underflow_callback(pa_stream *s, void *userdata)
-{
-       int dir = (s == stream[PLAY]) ? PLAY : REC;
-
-       dbg0("%s stream underrun", dir_str[dir]);
-}
-
-static void stream_overflow_callback(pa_stream *s, void *userdata)
-{
-       int dir = (s == stream[PLAY]) ? PLAY : REC;
-
-       dbg0("%s stream overrun", dir_str[dir]);
-}
-
-static size_t duration_to_bytes(size_t dur)
-{
-       return round_up(dur * sample_bps / 1000, frame_size);
-}
-
-static int prepare_streams(void)
-{
-       const struct stream_params *sp;
-       size_t min_frag_size, min_target_length, tmp;
-       int dir, rc;
-
-       /* nothing to do? */
-       if ((!stream_enabled[PLAY] || stream[PLAY]) &&
-           (!stream_enabled[REC] || stream[REC]))
-               return 0;
-
-       /* determine sample parameters */
-       sample_bps = pa_bytes_per_second(&sample_spec);
-       frame_size = pa_frame_size(&sample_spec);
-
-       sp = &stream_params[PLAY];
-       if (stream_enabled[REC])
-               sp = &stream_params[REC];
-
-       min_frag_size = duration_to_bytes(sp->min_process);
-       min_target_length = duration_to_bytes(sp->min_latency);
-
-       /* determine frag_size */
-       if (user_frag_size % frame_size) {
-               warn("requested frag_size (%zu) isn't multiple of frame (%zu)",
-                    user_frag_size, frame_size);
-               user_frag_size = round_up(user_frag_size, frame_size);
-       }
-
-       if (user_subdivision)
-               user_frag_size = round_up(sample_bps / user_subdivision,
-                                         frame_size);
-
-       if (user_frag_size) {
-               frag_size = user_frag_size;
-               if (frag_size < min_frag_size) {
-                       dbg0("requested frag_size (%zu) is smaller than "
-                            "minimum (%zu)", frag_size, min_frag_size);
-                       frag_size = min_frag_size;
-               }
-       } else {
-               tmp = round_up(sp->dfl_process * sample_bps / 1000, frame_size);
-               frag_size = tmp;
-               /* if frame_size is power of two, make frag_size so too */
-               if (is_power2(frame_size)) {
-                       frag_size = frame_size;
-                       while (frag_size < tmp)
-                               frag_size <<= 1;
-               }
-               user_frag_size = frag_size;
-       }
-
-       /* determine target and max length */
-       if (user_max_frags) {
-               target_length = user_max_frags * user_frag_size;
-               if (target_length < min_target_length) {
-                       dbg0("requested target_length (%zu) is smaller than "
-                            "minimum (%zu)", target_length, min_target_length);
-                       target_length = min_target_length;
-               }
-       } else {
-               tmp = round_up(sp->dfl_latency * sample_bps / 1000, frag_size);
-               target_length = tmp;
-               /* if frag_size is power of two, make target_length so
-                * too and align it to page_size.
-                */
-               if (is_power2(frag_size)) {
-                       target_length = frag_size;
-                       while (target_length < max(tmp, page_size))
-                               target_length <<= 1;
-               }
-               user_max_frags = target_length / frag_size;
-       }
-
-       user_max_length = user_frag_size * user_max_frags;
-       max_length = target_length + 2 * frag_size;
-
-       /* If mmapped, create backend stream with fixed parameters to
-        * create illusion of hardware buffer with acceptable latency.
-        */
-       if (mmapped()) {
-               /* set parameters for backend streams */
-               frag_size = duration_to_bytes(sp->mmap_process);
-               target_length = duration_to_bytes(sp->mmap_latency);
-               max_length = target_length + frag_size;
-               prebuf_size = 0;
-
-               mmap_size = round_down(mmap_raw_size, frame_size);
-               if (mmap_size != mmap_raw_size)
-                       warn("mmap_raw_size (%zu) unaligned to frame_size "
-                            "(%zu), mmap_size adjusted to %zu",
-                            mmap_raw_size, frame_size, mmap_size);
-       } else {
-               prebuf_size = min(user_frag_size * 2, user_max_length / 2);
-               prebuf_size = round_down(prebuf_size, frame_size);
-       }
-
-       for (dir = 0; dir < 2; dir++) {
-               pa_buffer_attr new_ba = { };
-               char buf[128];
-               pa_stream *s;
-               pa_stream_flags_t flags;
-               int vol[2];
-               size_t size;
-
-               if (!stream_enabled[dir] || stream[dir])
-                       continue;
-
-               dbg0("CREATE %s %s fsz=%zu:%zu", dir_str[dir],
-                    pa_sample_spec_snprint(buf, sizeof(buf), &sample_spec),
-                    frag_size, frag_size * 1000 / sample_bps);
-               dbg0("  tlen=%zu:%zu max=%zu:%zu pre=%zu:%zu",
-                    target_length, target_length * 1000 / sample_bps,
-                    max_length, max_length * 1000 / sample_bps,
-                    prebuf_size, prebuf_size * 1000 / sample_bps);
-               dbg0("  u_sd=%zu u_fsz=%zu:%zu u_maxf=%zu",
-                    user_subdivision, user_frag_size,
-                    user_frag_size * 1000 / sample_bps, user_max_frags);
-
-               channel_map = pa_channel_map_init_auto(&channel_map_stor,
-                                                      sample_spec.channels,
-                                                      PA_CHANNEL_MAP_OSS);
-
-               s = pa_stream_new(context, stream_name, &sample_spec,
-                                 channel_map);
-               if (!s) {
-                       err_pa("can't create streams");
-                       goto fail;
-               }
-               stream[dir] = s;
-
-               pa_stream_set_state_callback(s, stream_state_callback, NULL);
-               if (dir == PLAY) {
-                       pa_stream_set_write_callback(s,
-                                       stream_rw_callback, NULL);
-                       pa_stream_set_underflow_callback(s,
-                                       stream_underflow_callback, NULL);
-               } else {
-                       pa_stream_set_read_callback(s,
-                                       stream_rw_callback, NULL);
-                       pa_stream_set_overflow_callback(s,
-                                       stream_overflow_callback, NULL);
-               }
-
-               flags = PA_STREAM_AUTO_TIMING_UPDATE |
-                       PA_STREAM_INTERPOLATE_TIMING;
-               if (stream_corked[dir])
-                       flags |= PA_STREAM_START_CORKED;
-
-               new_ba.maxlength = max_length;
-               new_ba.tlength = target_length;
-               new_ba.prebuf = prebuf_size;
-               new_ba.minreq = frag_size;
-               new_ba.fragsize = frag_size;
-
-               if (dir == PLAY) {
-                       if (pa_stream_connect_playback(s, NULL, &new_ba, flags,
-                                                      NULL, NULL)) {
-                               err_pa("failed to connect playback stream");
-                               goto fail;
-                       }
-               } else {
-                       if (pa_stream_connect_record(s, NULL, &new_ba, flags)) {
-                               err_pa("failed to connect record stream");
-                               goto fail;
-                       }
-               }
-
-               while (pa_stream_get_state(s) == PA_STREAM_CREATING)
-                       pa_threaded_mainloop_wait(mainloop);
-               if (pa_stream_get_state(s) != PA_STREAM_READY) {
-                       err_pa("failed to connect stream (state=%d)",
-                              pa_stream_get_state(s));
-                       goto fail;
-               }
-
-               /* apply stored OSS volume */
-               memcpy(vol, stored_oss_vol[dir], sizeof(vol));
-               if (do_mixer(dir, vol))
-                       warn_pa("initial volume control failed");
-
-               /* stream is ready setup mmap stuff */
-               if (!mmap_map[dir])
-                       continue;
-
-               /* prep mmap staging buffer */
-               size = round_up(sp->mmap_staging * sample_bps / 1000,
-                               frag_size);
-               rc = ring_resize(&mmap_stg[dir], size);
-               if (rc)
-                       return rc;
-
-               mmap_idx[dir] = mmap_last_idx[dir] = get_mmap_idx(dir);
-               mmap_lead[dir] = round_up(sp->mmap_lead * sample_bps / 1000,
-                                         frame_size);
-               mmap_sync[dir] = 1;
-
-               /* apply the current trigger settings */
-               trigger_streams(-1, -1);
-       }
-
-       return 0;
- fail:
-       return padsp_done();
-}
-
-struct volume_ret {
-       int                     success;
-       pa_cvolume              *cv;
-};
-
-static void play_volume_callback(pa_context *c, const pa_sink_input_info *i,
-                                int eol, void *userdata)
-{
-       struct volume_ret *vr = userdata;
-
-       if (i) {
-               *vr->cv = i->volume;
-               vr->success = 1;
-       }
-       pa_threaded_mainloop_signal(mainloop, 0);
-}
-
-static void rec_volume_callback(pa_context *c, const pa_source_info *i,
-                               int eol, void *userdata)
-{
-       struct volume_ret *vr = userdata;
-
-       if (i) {
-               *vr->cv = i->volume;
-               vr->success = 1;
-       }
-       pa_threaded_mainloop_signal(mainloop, 0);
-}
-
-static int get_volume(int dir, pa_cvolume *cv)
-{
-       struct volume_ret vr = { .cv = cv };
-       uint32_t idx;
-
-       if (dir == PLAY) {
-               idx = pa_stream_get_index(stream[PLAY]);
-               EXEC_OP(pa_context_get_sink_input_info,
-                       context, idx, play_volume_callback, &vr);
-       } else {
-               idx = pa_stream_get_device_index(stream[REC]);
-               EXEC_OP(pa_context_get_source_info_by_index,
-                       context, idx, rec_volume_callback, &vr);
-       }
-       if (!vr.success) {
-               warn_pa("failed to get %s volume", dir_str[dir]);
-               return -EIO;
-       }
-       return 0;
-}
-
-static int set_volume(int dir, pa_cvolume *cv)
-{
-       uint32_t idx;
-       int rc;
-
-       if (dir == PLAY) {
-               idx = pa_stream_get_index(stream[PLAY]);
-               rc = EXEC_CONTEXT_OP(pa_context_set_sink_input_volume,
-                                    context, idx, cv);
-       } else {
-               idx = pa_stream_get_device_index(stream[REC]);
-               rc = EXEC_CONTEXT_OP(pa_context_set_source_volume_by_index,
-                                    context, idx, cv);
-       }
-       return rc;
-}
-
-static int chan_left_right(int ch)
-{
-       if (!channel_map || channel_map->channels <= ch) {
-               switch (ch) {
-               case 0:
-                       return LEFT;
-               case 1:
-                       return RIGHT;
-               default:
-                       return -1;
-               }
-       }
-
-       switch (channel_map->map[ch]) {
-       /*case PA_CHANNEL_POSITION_LEFT:*/      /* same as FRONT_LEFT */
-       case PA_CHANNEL_POSITION_FRONT_LEFT:
-       case PA_CHANNEL_POSITION_REAR_LEFT:
-       case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
-       case PA_CHANNEL_POSITION_SIDE_LEFT:
-       case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
-       case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
-               return LEFT;
-       /*case PA_CHANNEL_POSITION_RIGHT:*/     /* same as FRONT_RIGHT */
-       case PA_CHANNEL_POSITION_FRONT_RIGHT:
-       case PA_CHANNEL_POSITION_REAR_RIGHT:
-       case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
-       case PA_CHANNEL_POSITION_SIDE_RIGHT:
-       case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
-       case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
-               return RIGHT;
-       default:
-               return -1;
-       }
-}
-
-static int do_mixer(int dir, int *vol)
-{
-       pa_cvolume cv;
-       unsigned lv, rv;
-       int i, rc;
-
-       if (vol[0] >= 0) {
-               int avg;
-
-               stored_oss_vol[dir][LEFT] = vol[LEFT];
-               stored_oss_vol[dir][RIGHT] = vol[RIGHT];
-               vol[LEFT] = vol[LEFT] * PA_VOLUME_NORM / 100;
-               vol[RIGHT] = vol[RIGHT] * PA_VOLUME_NORM / 100;
-               avg = (vol[LEFT] + vol[RIGHT]) / 2;
-
-               pa_cvolume_mute(&cv, sample_spec.channels);
-
-               for (i = 0; i < cv.channels; i++)
-                       switch (chan_left_right(i)) {
-                       case LEFT:      cv.values[i] = vol[LEFT];       break;
-                       case RIGHT:     cv.values[i] = vol[RIGHT];      break;
-                       default:        cv.values[i] = avg;             break;
-                       }
-
-               rc = set_volume(dir, &cv);
-               if (rc)
-                       return rc;
-       }
-
-       rc = get_volume(dir, &cv);
-       if (rc)
-               return rc;
-
-       if (cv.channels == 1)
-               lv = rv = pa_cvolume_avg(&cv);
-       else {
-               unsigned lcnt = 0, rcnt = 0;
-
-               for (i = 0, lv = 0, rv = 0; i < cv.channels; i++)
-                       switch (chan_left_right(i)) {
-                       case LEFT:      lv += cv.values[i];     lcnt++; break;
-                       case RIGHT:     rv += cv.values[i];     rcnt++; break;
-                       }
-
-               if (lcnt)
-                       lv /= lcnt;
-               if (rcnt)
-                       rv /= rcnt;
-       }
-
-       vol[LEFT] = lv * 100 / PA_VOLUME_NORM;
-       vol[RIGHT] = rv * 100 / PA_VOLUME_NORM;
-
-       return 0;
-}
-
-static ssize_t padsp_mixer(enum ossp_opcode opcode,
-                          void *carg, void *din, size_t din_sz,
-                          void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       struct ossp_mixer_arg *arg = carg;
-       int i, rc[2] = { };
-
-       if (prepare_streams())
-               return -EIO;
-
-       for (i = 0; i < 2; i++)
-               if (stream[i])
-                       rc[i] = do_mixer(i, arg->vol[i]);
-               else
-                       memset(arg->vol[i], -1, sizeof(arg->vol[i]));
-
-       *(struct ossp_mixer_arg *)rarg = *arg;
-       return rc[0] ?: rc[1];
-}
-
-static void context_state_callback(pa_context *cxt, void *userdata)
-{
-       pa_threaded_mainloop_signal(mainloop, 0);
-}
-
-static void context_subscribe_callback(pa_context *context,
-                                      pa_subscription_event_type_t type,
-                                      uint32_t idx, void *userdata)
-{
-       struct ossp_notify event = { .magic = OSSP_NOTIFY_MAGIC,
-                                    .opcode = OSSP_NOTIFY_VOLCHG };
-       ssize_t ret;
-
-       if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) !=
-           PA_SUBSCRIPTION_EVENT_CHANGE)
-               return;
-
-       ret = write(ossp_notify_fd, &event, sizeof(event));
-       if (ret != sizeof(event) && errno != EPIPE)
-               warn_e(-errno, "write to notify_fd failed");
-}
-
-static ssize_t padsp_open(enum ossp_opcode opcode,
-                         void *carg, void *din, size_t din_sz,
-                         void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       struct ossp_dsp_open_arg *arg = carg;
-       char host_name[128] = "(unknown)", opener[128] = "(unknown)";
-       int state;
-
-       switch (arg->flags & O_ACCMODE) {
-       case O_WRONLY:
-               stream_enabled[PLAY] = 1;
-               break;
-       case O_RDONLY:
-               stream_enabled[REC] = 1;
-               break;
-       case O_RDWR:
-               stream_enabled[PLAY] = 1;
-               stream_enabled[REC] = 1;
-               break;
-       default:
-               assert(0);
-       }
-
-       /* determine stream name */
-       gethostname(host_name, sizeof(host_name) - 1);
-       snprintf(stream_name, sizeof(stream_name), "OSS Proxy %s/%s:%ld",
-                host_name, ossp_user_name, (long)arg->opener_pid);
-
-       /* create and connect PA context */
-       get_proc_self_info(arg->opener_pid, NULL, opener, sizeof(opener));
-       context = pa_context_new(mainloop_api, opener);
-       if (!context) {
-               err("pa_context_new() failed");
-               return -EIO;
-       }
-
-       pa_context_set_state_callback(context, context_state_callback, NULL);
-       pa_context_set_subscribe_callback(context, context_subscribe_callback,
-                                         NULL);
-
-       pa_context_connect(context, NULL, 0, NULL);
-       while (1) {
-               state = pa_context_get_state(context);
-               if (state != PA_CONTEXT_CONNECTING &&
-                   state != PA_CONTEXT_AUTHORIZING &&
-                   state != PA_CONTEXT_SETTING_NAME)
-                       break;
-
-               pa_threaded_mainloop_wait(mainloop);
-       }
-
-       if (EXEC_CONTEXT_OP(pa_context_subscribe, context,
-                           PA_SUBSCRIPTION_MASK_SINK_INPUT |
-                           PA_SUBSCRIPTION_MASK_SOURCE))
-               warn_pa("failed to subscribe to context events");
-
-       if (state != PA_CONTEXT_READY) {
-               err_pa("failed to connect context, state=%d", state);
-               return -EIO;
-       }
-
-       return 0;
-}
-
-static void mmap_fill_pstg(void)
-{
-       struct ring_buf *stg = &mmap_stg[PLAY];
-       struct ring_buf mmap;
-       uint64_t new_idx = get_mmap_idx(PLAY);
-       size_t bytes, space, size;
-       void *data;
-
-       if (new_idx <= mmap_idx[PLAY])
-               return;
-
-       bytes = new_idx - mmap_idx[PLAY];
-       space = ring_space(stg);
-
-       if (bytes > mmap_size) {
-               dbg0("mmap playback transfer chunk bigger than "
-                    "mmap size (bytes=%zu mmap_size=%zu)", bytes, mmap_size);
-               mmap_sync[PLAY] = 1;
-               bytes = mmap_size;
-       }
-
-       if (bytes > space) {
-               dbg0("mmap playback staging buffer overflow "
-                    "(bytes=%zu space=%zu)", bytes, space);
-               mmap_sync[PLAY] = 1;
-               bytes = space;
-       }
-
-       ring_manual_init(&mmap, mmap_map[PLAY], mmap_size,
-                        new_idx % mmap_size, bytes);
-
-       while ((data = ring_data(&mmap, &size))) {
-               ring_fill(stg, data, size);
-               ring_consume(&mmap, size);
-       }
-
-       mmap_idx[PLAY] = new_idx;
-}
-
-static void mmap_consume_rstg(void)
-{
-       struct ring_buf *stg = &mmap_stg[REC];
-       struct ring_buf mmap;
-       uint64_t new_idx = get_mmap_idx(REC);
-       uint64_t fill_idx = mmap_idx[REC];
-       size_t bytes, space;
-
-       if (new_idx <= mmap_idx[REC])
-               return;
-
-       space = new_idx - mmap_idx[REC];        /* mmapped space to fill in */
-       bytes = ring_bytes(stg);                /* recorded bytes in staging */ 
-
-       if (space > bytes) {
-               if (!mmap_sync[REC])
-                       dbg0("mmap recording staging buffer underflow "
-                            "(space=%zu bytes=%zu)", space, bytes);
-               mmap_sync[REC] = 1;
-       }
-
-       if (space > mmap_size) {
-               if (!mmap_sync[REC])
-                       dbg0("mmap recording transfer chunk bigger than "
-                            "mmap size (space=%zu mmap_size=%zu)",
-                            bytes, mmap_size);
-               mmap_sync[REC] = 1;
-               space = mmap_size;
-       }
-
-       /* If resync is requested, leave lead bytes in the staging
-        * buffer and copy everything else such that data is filled
-        * upto the new_idx.  If there are more bytes in staging than
-        * available space, those will be dropped.
-        */
-       if (mmap_sync[REC]) {
-               ssize_t avail = bytes - mmap_lead[REC];
-
-               /* make sure we always have lead bytes in staging */
-               if (avail < 0)
-                       goto skip;
-
-               if (avail > space) {
-                       dbg0("dropping %zu bytes from record staging buffer",
-                            avail - space);
-                       ring_consume(&mmap_stg[REC], avail - space);
-                       avail = space;
-               } else {
-                       dbg0("skippping %zu bytes in record mmap map",
-                            space - avail);
-                       space = avail;
-               }
-
-               assert(new_idx >= avail);
-               fill_idx = new_idx - avail;
-               mmap_sync[REC] = 0;
-       }
-
-       ring_manual_init(&mmap, mmap_map[REC], mmap_size,
-                        fill_idx % mmap_size, 0);
-
-       while (space) {
-               void *data;
-               size_t size, todo;
-
-               data = ring_data(stg, &size);
-               assert(data);
-
-               todo = min(size, space);
-               ring_fill(&mmap, data, todo);
-
-               ring_consume(stg, todo);
-               space -= todo;
-       }
-
- skip:
-       mmap_idx[REC] = new_idx;
-}
-
-static void do_mmap_write(size_t space)
-{
-       struct ring_buf *stg = &mmap_stg[PLAY];
-       size_t todo;
-       void *data;
-
-       space = round_down(space, frame_size);
-       mmap_fill_pstg();
-
-       while (space && (data = ring_data(stg, &todo))) {
-               pa_seek_mode_t mode = PA_SEEK_RELATIVE_END;
-               int64_t offset = 0;
-
-               todo = min(todo, space);
-
-               if (mmap_sync[PLAY]) {
-                       mode = PA_SEEK_RELATIVE_ON_READ;
-                       offset = (int64_t)mmap_lead[PLAY] - ring_bytes(stg);
-                       dbg0("mmap resync, offset=%ld", (long)offset);
-               }
-
-               if (pa_stream_write(stream[PLAY], data, todo, NULL,
-                                   offset, mode) < 0) {
-                       err_pa("pa_stream_write() failed");
-                       padsp_done();
-                       return;
-               }
-
-               mmap_sync[PLAY] = 0;
-               ring_consume(stg, todo);
-               space -= todo;
-       }
-}
-
-static void do_mmap_read(size_t bytes)
-{
-       struct ring_buf *stg = &mmap_stg[REC];
-
-       bytes = round_down(bytes, frame_size);
-       mmap_consume_rstg();
-
-       while (bytes) {
-               const void *peek_data;
-               size_t size;
-
-               if (pa_stream_peek(stream[REC], &peek_data, &size)) {
-                       err_pa("pa_stream_peek() failed");
-                       padsp_done();
-                       return;
-               }
-
-               if (!peek_data)
-                       break;
-
-               if (size <= ring_space(stg))
-                       ring_fill(stg, peek_data, size);
-               else {
-                       if (!mmap_sync[REC])
-                               dbg0("recording staging buffer overflow, "
-                                    "requesting resync");
-                       mmap_sync[REC] = 1;
-               }
-
-               pa_stream_drop(stream[REC]);
-               bytes -= size;
-       }
-}
-
-static void stream_rw_callback(pa_stream *s, size_t length, void *userdata)
-{
-       int dir;
-       size_t size;
-
-       if (s == stream[PLAY]) {
-               dir = PLAY;
-               size = pa_stream_writable_size(s);
-               if (mmap_map[PLAY])
-                       do_mmap_write(size);
-       } else if (s == stream[REC]) {
-               dir = REC;
-               size = pa_stream_readable_size(s);
-               if (mmap_map[REC])
-                       do_mmap_read(size);
-       } else {
-               dbg0("stream_rw_callback(): unknown stream %p PLAY/REC=%p/%p\n",
-                    s, stream[PLAY], stream[REC]);
-               return;
-       }
-
-       if (size < user_frag_size)
-               return;
-       if (stream_waiting)
-               pa_threaded_mainloop_signal(mainloop, 0);
-       if (stream_notify) {
-               struct ossp_notify event = { .magic = OSSP_NOTIFY_MAGIC,
-                                            .opcode = OSSP_NOTIFY_POLL };
-               ssize_t ret;
-
-               ret = write(ossp_notify_fd, &event, sizeof(event));
-               if (ret != sizeof(event)) {
-                       if (errno != EPIPE)
-                               err_e(-errno, "write to notify_fd failed");
-
-                       /* This function is run from PA mainloop and
-                        * thus the following padsp_done() won't be
-                        * noticed before the mainthread tries to run
-                        * the next command.  Well, that's good enough.
-                        */
-                       padsp_done();
-               }
-               stream_notify = 0;
-       }
-}
-
-static ssize_t padsp_write(enum ossp_opcode opcode,
-                          void *carg, void *din, size_t din_sz,
-                          void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       struct ossp_dsp_rw_arg *arg = carg;
-       size_t size;
-
-       if (prepare_streams() || !stream[PLAY])
-               return -EIO;
-
-       stream_waiting++;
-       while (1) {
-               size = pa_stream_writable_size(stream[PLAY]);
-               if (arg->nonblock || size >= user_frag_size)
-                       break;
-               pa_threaded_mainloop_wait(mainloop);
-       }
-       stream_waiting--;
-
-       size = round_down(size, user_frag_size);
-       if (!size)
-               return -EAGAIN;
-
-       size = min(size, din_sz);
-
-       if (pa_stream_write(stream[PLAY], din, size, NULL,
-                           0, PA_SEEK_RELATIVE) < 0) {
-               err_pa("pa_stream_write() failed");
-               return padsp_done();
-       }
-
-       return size;
-}
-
-static ssize_t padsp_read(enum ossp_opcode opcode,
-                         void *carg, void *din, size_t din_sz,
-                         void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       struct ossp_dsp_rw_arg *arg = carg;
-       size_t size;
-       void *data;
-
-       if (prepare_streams() || !stream[REC])
-               return -EIO;
- again:
-       if (!arg->nonblock) {
-               stream_waiting++;
-               while (1) {
-                       size = pa_stream_readable_size(stream[REC]);
-                       if (size + ring_bytes(&rec_buf) >= user_frag_size)
-                               break;
-                       pa_threaded_mainloop_wait(mainloop);
-               }
-               stream_waiting--;
-       }
-
-       while (ring_bytes(&rec_buf) < max(user_frag_size, *dout_szp)) {
-               const void *peek_data;
-
-               if (pa_stream_peek(stream[REC], &peek_data, &size) < 0) {
-                       err_pa("pa_stream_peek() failed");
-                       return padsp_done();
-               }
-
-               if (!peek_data)
-                       break;
-
-               if (ring_space(&rec_buf) < size) {
-                       size_t bufsz;
-
-                       bufsz = ring_size(&rec_buf);
-                       bufsz = max(2 * bufsz, bufsz + 2 * size);
-
-                       if (ring_resize(&rec_buf, bufsz)) {
-                               err("failed to allocate recording buffer");
-                               return padsp_done();
-                       }
-               }
-
-               ring_fill(&rec_buf, peek_data, size);
-               pa_stream_drop(stream[REC]);
-       }
-
-       size = round_down(ring_bytes(&rec_buf), user_frag_size);
-       if (!size) {
-               if (arg->nonblock)
-                       return -EAGAIN;
-               else
-                       goto again;
-       }
-
-       *dout_szp = size = min(size, *dout_szp);
-
-       while (size) {
-               size_t cnt;
-
-               data = ring_data(&rec_buf, &cnt);
-               assert(data);
-
-               cnt = min(size, cnt);
-               memcpy(dout, data, cnt);
-               ring_consume(&rec_buf, cnt);
-               dout += cnt;
-               size -= cnt;
-       }
-
-       return *dout_szp;
-}
-
-static ssize_t padsp_poll(enum ossp_opcode opcode,
-                         void *carg, void *din, size_t din_sz,
-                         void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       unsigned revents = 0;
-
-       if (prepare_streams() < 0)
-               return -EIO;
-
-       stream_notify |= *(int *)carg;
-
-       if (stream[PLAY] &&
-           pa_stream_writable_size(stream[PLAY]) >= user_frag_size)
-               revents |= POLLOUT;
-       if (stream[REC] &&
-           pa_stream_readable_size(stream[REC]) >= user_frag_size)
-               revents |= POLLIN;
-
-       *(unsigned *)rarg = revents;
-       return 0;
-}
-
-static ssize_t padsp_mmap(enum ossp_opcode opcode,
-                         void *carg, void *din, size_t din_sz,
-                         void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       struct ossp_dsp_mmap_arg *arg = carg;
-       int dir = arg->dir;
-
-       assert(!mmap_map[dir]);
-
-       kill_streams();
-
-       /* arg->size is rounded up to the nearest page boundary.
-        * There is no way to tell what the actual requested value is
-        * but assume that it was the reported buffer space if it
-        * falls into the same page aligned range.
-        */
-       mmap_raw_size = arg->size;
-       if (user_max_length && user_max_length < mmap_raw_size &&
-           round_up(mmap_raw_size, page_size) ==
-           round_up(user_max_length, page_size)) {
-               info("MMAP adjusting raw_size %zu -> %zu",
-                    mmap_raw_size, user_max_length);
-               mmap_raw_size = user_max_length;
-       }
-
-       dbg0("MMAP server-addr=%p sz=%zu", ossp_mmap_addr[dir], mmap_raw_size);
-
-       mmap_map[dir] = ossp_mmap_addr[dir];
-
-       /* if mmapped, only mmapped streams are enabled */
-       stream_enabled[PLAY] = !!mmap_map[PLAY];
-       stream_enabled[REC] = !!mmap_map[REC];
-
-       return 0;
-}
-
-static ssize_t padsp_munmap(enum ossp_opcode opcode,
-                           void *carg, void *din, size_t din_sz,
-                           void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       int dir = *(int *)carg;
-
-       assert(mmap_map[dir]);
-       kill_streams();
-       mmap_map[dir] = NULL;
-       return 0;
-}
-
-static ssize_t padsp_flush(enum ossp_opcode opcode,
-                          void *carg, void *din, size_t din_sz,
-                          void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       flush_streams(opcode == OSSP_DSP_SYNC);
-       return 0;
-}
-
-static ssize_t padsp_post(enum ossp_opcode opcode,
-                         void *carg, void *din, size_t din_sz,
-                         void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       return trigger_streams(1, -1);
-}
-
-static ssize_t padsp_get_param(enum ossp_opcode opcode,
-                              void *carg, void *din, size_t din_sz,
-                              void *rarg, void *dout, size_t *dout_szp,
-                              int tfd)
-{
-       int v = 0;
-
-       switch (opcode) {
-       case OSSP_DSP_GET_RATE:
-               v = sample_spec.rate;
-               break;
-
-       case OSSP_DSP_GET_CHANNELS:
-               v = sample_spec.channels;
-               break;
-
-       case OSSP_DSP_GET_FORMAT:
-               v = fmt_pa_to_oss(sample_spec.format);
-               break;
-
-       case OSSP_DSP_GET_BLKSIZE:
-               if (prepare_streams() < 0)
-                       return -EIO;
-               v = user_frag_size;
-               break;
-
-       case OSSP_DSP_GET_FORMATS:
-               v = AFMT_U8 | AFMT_A_LAW | AFMT_MU_LAW | AFMT_S16_LE |
-                       AFMT_S16_BE | AFMT_FLOAT | AFMT_S32_LE | AFMT_S32_BE;
-               break;
-
-       case OSSP_DSP_GET_TRIGGER:
-               if (!stream_corked[PLAY])
-                       v |= PCM_ENABLE_OUTPUT;
-               if (!stream_corked[REC])
-                       v |= PCM_ENABLE_INPUT;
-               break;
-
-       default:
-               assert(0);
-       }
-
-       *(int *)rarg = v;
-
-       return 0;
-}
-
-static ssize_t padsp_set_param(enum ossp_opcode opcode,
-                              void *carg, void *din, size_t din_sz,
-                              void *rarg, void *dout, size_t *dout_szp,
-                              int tfd)
-{
-       pa_sample_spec new_spec = sample_spec;
-       int v = *(int *)carg;
-
-       /* kill the streams before changing parameters */
-       kill_streams();
-
-       switch (opcode) {
-       case OSSP_DSP_SET_RATE:
-               new_spec.rate = v;
-               if (pa_sample_spec_valid(&new_spec))
-                       sample_spec = new_spec;
-               v = sample_spec.rate;
-               break;
-
-       case OSSP_DSP_SET_CHANNELS:
-               new_spec.channels = v;
-               if (pa_sample_spec_valid(&new_spec))
-                       sample_spec = new_spec;
-               v = sample_spec.channels;
-               break;
-
-       case OSSP_DSP_SET_FORMAT:
-               new_spec.format = fmt_oss_to_pa(v);
-               if (pa_sample_spec_valid(&new_spec))
-                       sample_spec = new_spec;
-               v = fmt_pa_to_oss(sample_spec.format);
-               break;
-
-       case OSSP_DSP_SET_SUBDIVISION:
-               if (!v) {
-                       v = user_subdivision ?: 1;
-                       break;
-               }
-               user_frag_size= 0;
-               user_subdivision = v;
-               break;
-
-       case OSSP_DSP_SET_FRAGMENT:
-               user_subdivision = 0;
-               user_frag_size = 1 << (v & 0xffff);
-               user_max_frags = (v >> 16) & 0xffff;
-               if (user_frag_size < 4)
-                       user_frag_size = 4;
-               if (user_max_frags < 2)
-                       user_max_frags = 2;
-               break;
-       default:
-               assert(0);
-       }
-
-       if (rarg)
-               *(int *)rarg = v;
-       return 0;
-}
-
-static ssize_t padsp_set_trigger(enum ossp_opcode opcode,
-                                void *carg, void *din, size_t din_sz,
-                                void *rarg, void *dout, size_t *dout_szp,
-                                int fd)
-{
-       int enable = *(int *)carg;
-
-       return trigger_streams(enable & PCM_ENABLE_OUTPUT,
-                              enable & PCM_ENABLE_INPUT);
-}
-
-static ssize_t padsp_get_space(enum ossp_opcode opcode,
-                              void *carg, void *din, size_t din_sz,
-                              void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       int dir = (opcode == OSSP_DSP_GET_OSPACE) ? PLAY : REC;
-       struct audio_buf_info info = { };
-       int rc;
-
-       rc = prepare_streams();
-       if (rc)
-               return -EIO;
-
-       if (mmapped()) {
-               info.fragments = mmap_raw_size / user_frag_size;
-               info.fragstotal = info.fragments;
-               info.fragsize = user_frag_size;
-               info.bytes = mmap_raw_size;
-       } else {
-               size_t space;
-
-               if (dir == PLAY)
-                       space = pa_stream_writable_size(stream[PLAY]);
-               else
-                       space = pa_stream_readable_size(stream[REC]);
-
-               space = round_down(space, user_frag_size);
-               space = min(space, user_frag_size * user_max_frags);
-
-               info.fragments = space / user_frag_size;
-               info.fragstotal = user_max_frags;
-               info.fragsize = user_frag_size;
-               info.bytes = space;
-       }
-
-       *(struct audio_buf_info *)rarg = info;
-       return 0;
-}
-
-static ssize_t padsp_get_ptr(enum ossp_opcode opcode,
-                            void *carg, void *din, size_t din_sz,
-                            void *rarg, void *dout, size_t *dout_szp, int tfd)
-{
-       int dir = (opcode == OSSP_DSP_GET_OPTR) ? PLAY : REC;
-       struct count_info info = { };
-
-       if (prepare_streams() < 0 || !stream[dir])
-               return -EIO;
-
-       if (mmap_map[dir]) {
-               /* mmap operation in progress, report mmap buffer parameters */
-               if (dir == PLAY)
-                       mmap_fill_pstg();
-               else
-                       mmap_consume_rstg();
-
-               info.bytes = mmap_idx[dir];
-               info.blocks = (mmap_idx[dir] - mmap_last_idx[dir]) / frame_size;
-               info.ptr = mmap_idx[dir] % mmap_size;
-
-               mmap_last_idx[dir] = mmap_idx[dir];
-       } else {
-               /* simulate pointers using timestamps */
-               double bpus = (double)sample_bps / 1000000;
-               size_t bytes, delta_bytes;
-               pa_usec_t usec, delta;
-
-               if (pa_stream_get_time(stream[dir], &usec) < 0) {
-                       warn_pa("pa_stream_get_time() failed");
-                       return -EIO;
-               }
-
-               delta = usec - stream_ptr_timestamp[dir];
-               stream_ptr_timestamp[dir] = usec;
-               bytes = bpus * usec;
-               delta_bytes = bpus * delta;
-
-               info.bytes = bytes & INT_MAX;
-               info.blocks = (delta_bytes + frame_size - 1) / frame_size;
-               info.ptr = bytes % user_max_length;
-       }
-
-       *(struct count_info *)rarg = info;
-       return 0;
-}
-
-static ssize_t padsp_get_odelay(enum ossp_opcode opcode,
-                               void *carg, void *din, size_t din_sz,
-                               void *rarg, void *dout, size_t *dout_szp,
-                               int fd)
-{
-       double bpus = (double)sample_bps / 1000000;
-       pa_usec_t usec;
-
-       if (prepare_streams() < 0 || !stream[PLAY])
-               return -EIO;
-
-       if (pa_stream_get_latency(stream[PLAY], &usec, NULL) < 0) {
-               warn_pa("pa_stream_get_latency() failed");
-               return -EIO;
-       }
-
-       *(int *)rarg = bpus * usec;
-       return 0;
-}
-
-static ossp_action_fn_t action_fn_tbl[OSSP_NR_OPCODES] = {
-       [OSSP_MIXER]            = padsp_mixer,
-       [OSSP_DSP_OPEN]         = padsp_open,
-       [OSSP_DSP_READ]         = padsp_read,
-       [OSSP_DSP_WRITE]        = padsp_write,
-       [OSSP_DSP_POLL]         = padsp_poll,
-       [OSSP_DSP_MMAP]         = padsp_mmap,
-       [OSSP_DSP_MUNMAP]       = padsp_munmap,
-       [OSSP_DSP_RESET]        = padsp_flush,
-       [OSSP_DSP_SYNC]         = padsp_flush,
-       [OSSP_DSP_POST]         = padsp_post,
-       [OSSP_DSP_GET_RATE]     = padsp_get_param,
-       [OSSP_DSP_GET_CHANNELS] = padsp_get_param,
-       [OSSP_DSP_GET_FORMAT]   = padsp_get_param,
-       [OSSP_DSP_GET_BLKSIZE]  = padsp_get_param,
-       [OSSP_DSP_GET_FORMATS]  = padsp_get_param,
-       [OSSP_DSP_SET_RATE]     = padsp_set_param,
-       [OSSP_DSP_SET_CHANNELS] = padsp_set_param,
-       [OSSP_DSP_SET_FORMAT]   = padsp_set_param,
-       [OSSP_DSP_SET_SUBDIVISION] = padsp_set_param,
-       [OSSP_DSP_SET_FRAGMENT] = padsp_set_param,
-       [OSSP_DSP_GET_TRIGGER]  = padsp_get_param,
-       [OSSP_DSP_SET_TRIGGER]  = padsp_set_trigger,
-       [OSSP_DSP_GET_OSPACE]   = padsp_get_space,
-       [OSSP_DSP_GET_ISPACE]   = padsp_get_space,
-       [OSSP_DSP_GET_OPTR]     = padsp_get_ptr,
-       [OSSP_DSP_GET_IPTR]     = padsp_get_ptr,
-       [OSSP_DSP_GET_ODELAY]   = padsp_get_odelay,
-};
-
-static int action_pre(void)
-{
-       pa_threaded_mainloop_lock(mainloop);
-       if (fail_code) {
-               pa_threaded_mainloop_unlock(mainloop);
-               return fail_code;
-       }
-       return 0;
-}
-
-static void action_post(void)
-{
-       pa_threaded_mainloop_unlock(mainloop);
-}
-
-int main(int argc, char **argv)
-{
-       int rc;
-
-       ossp_slave_init(argc, argv);
-
-       page_size = sysconf(_SC_PAGE_SIZE);
-
-       mainloop = pa_threaded_mainloop_new();
-       if (!mainloop) {
-               err("failed to allocate mainloop");
-               return 1;
-       }
-       mainloop_api = pa_threaded_mainloop_get_api(mainloop);
-
-       if (pa_threaded_mainloop_start(mainloop)) {
-               err("pa_mainloop_start() failed");
-               return 1;
-       }
-
-       /* Okay, now we're open for business */
-       rc = 0;
-       do {
-               rc = ossp_slave_process_command(ossp_cmd_fd, action_fn_tbl,
-                                               action_pre, action_post);
-       } while (rc > 0 && !fail_code);
-       if (rc)
-               fail_code = rc;
-
-       pa_threaded_mainloop_lock(mainloop);
-
-       kill_streams();
-       if (context) {
-               pa_context_disconnect(context);
-               pa_context_unref(context);
-       }
-
-       pa_threaded_mainloop_unlock(mainloop);
-
-       pa_threaded_mainloop_stop(mainloop);
-       pa_threaded_mainloop_free(mainloop);
-
-       return fail_code ? 1 : 0;
-}