add first version of an initscript
[osspd.git] / ossp-padsp.c
1 /*
2  * ossp-padsp - ossp DSP slave which forwards to pulseaduio
3  *
4  * Copyright (C) 2008-2010  SUSE Linux Products GmbH
5  * Copyright (C) 2008-2010  Tejun Heo <tj@kernel.org>
6  *
7  * This file is released under the GPLv2.
8  */
9
10 #include <assert.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <getopt.h>
14 #include <libgen.h>
15 #include <limits.h>
16 #include <poll.h>
17 #include <pthread.h>
18 #include <pwd.h>
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include <pulse/pulseaudio.h>
27 #include <sys/soundcard.h>
28
29 #include "ossp-slave.h"
30
31 enum {
32         AFMT_FLOAT              = 0x00004000,
33         AFMT_S32_LE             = 0x00001000,
34         AFMT_S32_BE             = 0x00002000,
35 };
36
37 /* everything is in millisecs */
38 struct stream_params {
39         size_t          min_process;
40         size_t          min_latency;
41         size_t          dfl_process;
42         size_t          dfl_latency;
43         size_t          mmap_process;
44         size_t          mmap_latency;
45         size_t          mmap_lead;
46         size_t          mmap_staging;
47 };
48
49 /* TODO: make this configurable */
50 static struct stream_params stream_params[] = {
51         [ PLAY ] = { .min_process = 25,         .min_latency = 100,
52                      .dfl_process = 50,         .dfl_latency = 200,
53                      .mmap_process = 25,        .mmap_latency = 50,
54                      .mmap_lead = 25,           .mmap_staging = 100 },
55         [ REC ]  = { .min_process = 25,         .min_latency = 200,
56                      .dfl_process = 50,         .dfl_latency = 400,
57                      .mmap_process = 25,        .mmap_latency = 50,
58                      .mmap_lead = 25,           .mmap_staging = 1000 },
59 };
60
61 static size_t page_size;
62 static pa_context *context;
63 static pa_threaded_mainloop *mainloop;
64 static pa_mainloop_api *mainloop_api;
65 static char stream_name[128];
66 static int stream_enabled[2];
67 static int stream_corked[2];
68 static int stream_waiting;
69 static int stream_notify;
70 static pa_channel_map channel_map_stor;
71 static pa_channel_map *channel_map;
72 static pa_stream *stream[2];
73 static pa_usec_t stream_ptr_timestamp[2];
74 static struct ring_buf rec_buf;
75 static int stored_oss_vol[2][2] = { { -1, -1 }, { -1, -1 } };
76 static int fail_code;
77
78 static pa_sample_spec sample_spec = {
79         .format = PA_SAMPLE_U8,
80         .rate = 8000,
81         .channels = 1,
82 };
83 static size_t sample_bps = 8000;
84 static size_t frame_size = 1;
85
86 /* user visible stream parameters */
87 static size_t user_frag_size;
88 static size_t user_subdivision; /* alternative way to determine frag_size */
89 static size_t user_max_frags;   /* maximum number of fragments */
90 static size_t user_max_length;
91
92 /* actual stream parameters */
93 static size_t frag_size;
94 static size_t target_length;
95 static size_t max_length;
96 static size_t prebuf_size;
97
98 /* mmap stuff */
99 static size_t mmap_raw_size, mmap_size;
100 static void *mmap_map[2];
101 static uint64_t mmap_idx[2];            /* mmap pointer */
102 static uint64_t mmap_last_idx[2];       /* last idx for get_ptr */
103 static struct ring_buf mmap_stg[2];     /* staging ring buffer */
104 static size_t mmap_lead[2];             /* lead bytes */
105 static int mmap_sync[2];                /* sync with backend stream */
106
107 static const char *dir_str[] = {
108         [PLAY]          = "PLAY",
109         [REC]           = "REC",
110 };
111
112 static void stream_rw_callback(pa_stream *s, size_t length, void *userdata);
113
114 #define __pa_err                pa_strerror(pa_context_errno(context))
115 #define dbg1_pa(fmt, args...)   dbg1(fmt" (%s)" , ##args, __pa_err)
116 #define dbg0_pa(fmt, args...)   dbg0(fmt" (%s)" , ##args, __pa_err)
117 #define info_pa(fmt, args...)   info(fmt" (%s)" , ##args, __pa_err)
118 #define warn_pa(fmt, args...)   warn(fmt" (%s)" , ##args, __pa_err)
119 #define err_pa(fmt, args...)    err(fmt" (%s)" , ##args, __pa_err)
120
121 #define round_down(v, t)        ((v) / (t) * (t))
122 #define round_up(v, t)          (((v) + (t) - 1) / (t) * (t))
123 #define is_power2(v)            !((v) & ((v) - 1))
124
125 static int do_mixer(int dir, int *vol);
126
127 static int padsp_done(void)
128 {
129         fail_code = -EIO;
130         mainloop_api->quit(mainloop_api, 1);
131         return fail_code;
132 }
133
134 static int fmt_oss_to_pa(int fmt)
135 {
136         switch (fmt) {
137         case AFMT_U8:                   return PA_SAMPLE_U8;
138         case AFMT_A_LAW:                return PA_SAMPLE_ALAW;
139         case AFMT_MU_LAW:               return PA_SAMPLE_ULAW;
140         case AFMT_S16_LE:               return PA_SAMPLE_S16LE;
141         case AFMT_S16_BE:               return PA_SAMPLE_S16BE;
142         case AFMT_FLOAT:                return PA_SAMPLE_FLOAT32NE;
143         case AFMT_S32_LE:               return PA_SAMPLE_S32LE;
144         case AFMT_S32_BE:               return PA_SAMPLE_S32BE;
145         default:                        return PA_SAMPLE_U8;
146         }
147 }
148
149 static int fmt_pa_to_oss(int fmt)
150 {
151         switch (fmt) {
152         case PA_SAMPLE_U8:              return AFMT_U8;
153         case PA_SAMPLE_ALAW:            return AFMT_A_LAW;
154         case PA_SAMPLE_ULAW:            return AFMT_MU_LAW;
155         case PA_SAMPLE_S16LE:           return AFMT_S16_LE;
156         case PA_SAMPLE_S16BE:           return AFMT_S16_BE;
157         case PA_SAMPLE_FLOAT32NE:       return AFMT_FLOAT;
158         case PA_SAMPLE_S32LE:           return AFMT_S32_LE;
159         case PA_SAMPLE_S32BE:           return AFMT_S32_BE;
160         default:                        return AFMT_U8;
161         }
162 }
163
164 #define EXEC_OP(op, args...)    do {                                    \
165         pa_operation *_o;                                               \
166         _o = op(args);                                                  \
167         if (_o) {                                                       \
168                 while (pa_operation_get_state(_o) != PA_OPERATION_DONE) \
169                         pa_threaded_mainloop_wait(mainloop);            \
170                 pa_operation_unref(_o);                                 \
171         } } while (0)
172
173 static void context_op_callback(pa_context *s, int success, void *userdata)
174 {
175         *(int *)userdata = success;
176         pa_threaded_mainloop_signal(mainloop, 0);
177 }
178
179 static void stream_op_callback(pa_stream *s, int success, void *userdata)
180 {
181         *(int *)userdata = success;
182         pa_threaded_mainloop_signal(mainloop, 0);
183 }
184
185 #define EXEC_CONTEXT_OP(op, args...) ({                                 \
186         int _success;                                                   \
187         EXEC_OP(op , ##args, context_op_callback, &_success);           \
188         if (!_success)                                                  \
189                 warn_pa("%s() failed", #op);                            \
190         _success ? 0 : -EIO; })
191
192 #define EXEC_STREAM_OP(op, args...) ({                                  \
193         int _success;                                                   \
194         EXEC_OP(op , ##args, stream_op_callback, &_success);            \
195         if (!_success)                                                  \
196                 warn_pa("%s() failed", #op);                            \
197         _success ? 0 : -EIO; })
198
199 static int mmapped(void)
200 {
201         return mmap_map[PLAY] || mmap_map[REC];
202 }
203
204 static uint64_t get_mmap_idx(int dir)
205 {
206         uint64_t idx;
207         pa_usec_t time;
208
209         if (!stream[dir])
210                 return mmap_idx[dir];
211
212         if (pa_stream_get_time(stream[dir], &time) < 0) {
213                 dbg1_pa("pa_stream_get_time() failed");
214                 return mmap_idx[dir];
215         }
216
217         /* calculate the current index from time elapsed */
218         idx = ((uint64_t)time * sample_bps / 1000000);
219         /* round down to the nearest frame boundary */
220         idx = idx / frame_size * frame_size;
221
222         return idx;
223 }
224
225 static void flush_streams(int drain)
226 {
227         int i;
228
229         if (!(stream[PLAY] || stream[REC]))
230                 return;
231
232         dbg0("FLUSH drain=%d", drain);
233
234         /* mmapped streams run forever, can't drain */
235         if (drain && !mmapped() && stream[PLAY])
236                 EXEC_STREAM_OP(pa_stream_drain, stream[PLAY]);
237
238         for (i = 0; i < 2; i++)
239                 if (stream[i])
240                         EXEC_STREAM_OP(pa_stream_flush, stream[i]);
241
242         ring_consume(&rec_buf, ring_bytes(&rec_buf));
243 }
244
245 static void kill_streams(void)
246 {
247         int dir;
248
249         if (!(stream[PLAY] || stream[REC]))
250                 return;
251
252         flush_streams(1);
253
254         dbg0("KILL");
255
256         for (dir = 0; dir < 2; dir++) {
257                 if (!stream[dir])
258                         continue;
259                 pa_stream_disconnect(stream[dir]);
260                 pa_stream_unref(stream[dir]);
261                 stream[dir] = NULL;
262                 stream_ptr_timestamp[dir] = 0;
263
264                 ring_consume(&mmap_stg[dir], ring_bytes(&mmap_stg[dir]));
265                 ring_resize(&mmap_stg[dir], 0);
266         }
267 }
268
269 static int trigger_streams(int play, int rec)
270 {
271         int ret = 0, dir, rc;
272
273         if (play >= 0)
274                 stream_corked[PLAY] = !play;
275         if (rec >= 0)
276                 stream_corked[REC] = !rec;
277
278         for (dir = 0; dir < 2; dir++) {
279                 if (!stream[dir])
280                         continue;
281
282                 rc = EXEC_STREAM_OP(pa_stream_cork, stream[dir],
283                                     stream_corked[dir]);
284                 if (!rc && dir == PLAY && !mmap_map[dir] && !stream_corked[dir])
285                         rc = EXEC_STREAM_OP(pa_stream_trigger, stream[dir]);
286                 if (!ret)
287                         ret = rc;
288         }
289
290         return ret;
291 }
292
293 static void stream_state_callback(pa_stream *s, void *userdata)
294 {
295         pa_threaded_mainloop_signal(mainloop, 0);
296 }
297
298 static void stream_underflow_callback(pa_stream *s, void *userdata)
299 {
300         int dir = (s == stream[PLAY]) ? PLAY : REC;
301
302         dbg0("%s stream underrun", dir_str[dir]);
303 }
304
305 static void stream_overflow_callback(pa_stream *s, void *userdata)
306 {
307         int dir = (s == stream[PLAY]) ? PLAY : REC;
308
309         dbg0("%s stream overrun", dir_str[dir]);
310 }
311
312 static size_t duration_to_bytes(size_t dur)
313 {
314         return round_up(dur * sample_bps / 1000, frame_size);
315 }
316
317 static int prepare_streams(void)
318 {
319         const struct stream_params *sp;
320         size_t min_frag_size, min_target_length, tmp;
321         int dir, rc;
322
323         /* nothing to do? */
324         if ((!stream_enabled[PLAY] || stream[PLAY]) &&
325             (!stream_enabled[REC] || stream[REC]))
326                 return 0;
327
328         /* determine sample parameters */
329         sample_bps = pa_bytes_per_second(&sample_spec);
330         frame_size = pa_frame_size(&sample_spec);
331
332         sp = &stream_params[PLAY];
333         if (stream_enabled[REC])
334                 sp = &stream_params[REC];
335
336         min_frag_size = duration_to_bytes(sp->min_process);
337         min_target_length = duration_to_bytes(sp->min_latency);
338
339         /* determine frag_size */
340         if (user_frag_size % frame_size) {
341                 warn("requested frag_size (%zu) isn't multiple of frame (%zu)",
342                      user_frag_size, frame_size);
343                 user_frag_size = round_up(user_frag_size, frame_size);
344         }
345
346         if (user_subdivision)
347                 user_frag_size = round_up(sample_bps / user_subdivision,
348                                           frame_size);
349
350         if (user_frag_size) {
351                 frag_size = user_frag_size;
352                 if (frag_size < min_frag_size) {
353                         dbg0("requested frag_size (%zu) is smaller than "
354                              "minimum (%zu)", frag_size, min_frag_size);
355                         frag_size = min_frag_size;
356                 }
357         } else {
358                 tmp = round_up(sp->dfl_process * sample_bps / 1000, frame_size);
359                 frag_size = tmp;
360                 /* if frame_size is power of two, make frag_size so too */
361                 if (is_power2(frame_size)) {
362                         frag_size = frame_size;
363                         while (frag_size < tmp)
364                                 frag_size <<= 1;
365                 }
366                 user_frag_size = frag_size;
367         }
368
369         /* determine target and max length */
370         if (user_max_frags) {
371                 target_length = user_max_frags * user_frag_size;
372                 if (target_length < min_target_length) {
373                         dbg0("requested target_length (%zu) is smaller than "
374                              "minimum (%zu)", target_length, min_target_length);
375                         target_length = min_target_length;
376                 }
377         } else {
378                 tmp = round_up(sp->dfl_latency * sample_bps / 1000, frag_size);
379                 target_length = tmp;
380                 /* if frag_size is power of two, make target_length so
381                  * too and align it to page_size.
382                  */
383                 if (is_power2(frag_size)) {
384                         target_length = frag_size;
385                         while (target_length < max(tmp, page_size))
386                                 target_length <<= 1;
387                 }
388                 user_max_frags = target_length / frag_size;
389         }
390
391         user_max_length = user_frag_size * user_max_frags;
392         max_length = target_length + 2 * frag_size;
393
394         /* If mmapped, create backend stream with fixed parameters to
395          * create illusion of hardware buffer with acceptable latency.
396          */
397         if (mmapped()) {
398                 /* set parameters for backend streams */
399                 frag_size = duration_to_bytes(sp->mmap_process);
400                 target_length = duration_to_bytes(sp->mmap_latency);
401                 max_length = target_length + frag_size;
402                 prebuf_size = 0;
403
404                 mmap_size = round_down(mmap_raw_size, frame_size);
405                 if (mmap_size != mmap_raw_size)
406                         warn("mmap_raw_size (%zu) unaligned to frame_size "
407                              "(%zu), mmap_size adjusted to %zu",
408                              mmap_raw_size, frame_size, mmap_size);
409         } else {
410                 prebuf_size = min(user_frag_size * 2, user_max_length / 2);
411                 prebuf_size = round_down(prebuf_size, frame_size);
412         }
413
414         for (dir = 0; dir < 2; dir++) {
415                 pa_buffer_attr new_ba = { };
416                 char buf[128];
417                 pa_stream *s;
418                 pa_stream_flags_t flags;
419                 int vol[2];
420                 size_t size;
421
422                 if (!stream_enabled[dir] || stream[dir])
423                         continue;
424
425                 dbg0("CREATE %s %s fsz=%zu:%zu", dir_str[dir],
426                      pa_sample_spec_snprint(buf, sizeof(buf), &sample_spec),
427                      frag_size, frag_size * 1000 / sample_bps);
428                 dbg0("  tlen=%zu:%zu max=%zu:%zu pre=%zu:%zu",
429                      target_length, target_length * 1000 / sample_bps,
430                      max_length, max_length * 1000 / sample_bps,
431                      prebuf_size, prebuf_size * 1000 / sample_bps);
432                 dbg0("  u_sd=%zu u_fsz=%zu:%zu u_maxf=%zu",
433                      user_subdivision, user_frag_size,
434                      user_frag_size * 1000 / sample_bps, user_max_frags);
435
436                 channel_map = pa_channel_map_init_auto(&channel_map_stor,
437                                                        sample_spec.channels,
438                                                        PA_CHANNEL_MAP_OSS);
439
440                 s = pa_stream_new(context, stream_name, &sample_spec,
441                                   channel_map);
442                 if (!s) {
443                         err_pa("can't create streams");
444                         goto fail;
445                 }
446                 stream[dir] = s;
447
448                 pa_stream_set_state_callback(s, stream_state_callback, NULL);
449                 if (dir == PLAY) {
450                         pa_stream_set_write_callback(s,
451                                         stream_rw_callback, NULL);
452                         pa_stream_set_underflow_callback(s,
453                                         stream_underflow_callback, NULL);
454                 } else {
455                         pa_stream_set_read_callback(s,
456                                         stream_rw_callback, NULL);
457                         pa_stream_set_overflow_callback(s,
458                                         stream_overflow_callback, NULL);
459                 }
460
461                 flags = PA_STREAM_AUTO_TIMING_UPDATE |
462                         PA_STREAM_INTERPOLATE_TIMING;
463                 if (stream_corked[dir])
464                         flags |= PA_STREAM_START_CORKED;
465
466                 new_ba.maxlength = max_length;
467                 new_ba.tlength = target_length;
468                 new_ba.prebuf = prebuf_size;
469                 new_ba.minreq = frag_size;
470                 new_ba.fragsize = frag_size;
471
472                 if (dir == PLAY) {
473                         if (pa_stream_connect_playback(s, NULL, &new_ba, flags,
474                                                        NULL, NULL)) {
475                                 err_pa("failed to connect playback stream");
476                                 goto fail;
477                         }
478                 } else {
479                         if (pa_stream_connect_record(s, NULL, &new_ba, flags)) {
480                                 err_pa("failed to connect record stream");
481                                 goto fail;
482                         }
483                 }
484
485                 while (pa_stream_get_state(s) == PA_STREAM_CREATING)
486                         pa_threaded_mainloop_wait(mainloop);
487                 if (pa_stream_get_state(s) != PA_STREAM_READY) {
488                         err_pa("failed to connect stream (state=%d)",
489                                pa_stream_get_state(s));
490                         goto fail;
491                 }
492
493                 /* apply stored OSS volume */
494                 memcpy(vol, stored_oss_vol[dir], sizeof(vol));
495                 if (do_mixer(dir, vol))
496                         warn_pa("initial volume control failed");
497
498                 /* stream is ready setup mmap stuff */
499                 if (!mmap_map[dir])
500                         continue;
501
502                 /* prep mmap staging buffer */
503                 size = round_up(sp->mmap_staging * sample_bps / 1000,
504                                 frag_size);
505                 rc = ring_resize(&mmap_stg[dir], size);
506                 if (rc)
507                         return rc;
508
509                 mmap_idx[dir] = mmap_last_idx[dir] = get_mmap_idx(dir);
510                 mmap_lead[dir] = round_up(sp->mmap_lead * sample_bps / 1000,
511                                           frame_size);
512                 mmap_sync[dir] = 1;
513
514                 /* apply the current trigger settings */
515                 trigger_streams(-1, -1);
516         }
517
518         return 0;
519  fail:
520         return padsp_done();
521 }
522
523 struct volume_ret {
524         int                     success;
525         pa_cvolume              *cv;
526 };
527
528 static void play_volume_callback(pa_context *c, const pa_sink_input_info *i,
529                                  int eol, void *userdata)
530 {
531         struct volume_ret *vr = userdata;
532
533         if (i) {
534                 *vr->cv = i->volume;
535                 vr->success = 1;
536         }
537         pa_threaded_mainloop_signal(mainloop, 0);
538 }
539
540 static void rec_volume_callback(pa_context *c, const pa_source_info *i,
541                                 int eol, void *userdata)
542 {
543         struct volume_ret *vr = userdata;
544
545         if (i) {
546                 *vr->cv = i->volume;
547                 vr->success = 1;
548         }
549         pa_threaded_mainloop_signal(mainloop, 0);
550 }
551
552 static int get_volume(int dir, pa_cvolume *cv)
553 {
554         struct volume_ret vr = { .cv = cv };
555         uint32_t idx;
556
557         if (dir == PLAY) {
558                 idx = pa_stream_get_index(stream[PLAY]);
559                 EXEC_OP(pa_context_get_sink_input_info,
560                         context, idx, play_volume_callback, &vr);
561         } else {
562                 idx = pa_stream_get_device_index(stream[REC]);
563                 EXEC_OP(pa_context_get_source_info_by_index,
564                         context, idx, rec_volume_callback, &vr);
565         }
566         if (!vr.success) {
567                 warn_pa("failed to get %s volume", dir_str[dir]);
568                 return -EIO;
569         }
570         return 0;
571 }
572
573 static int set_volume(int dir, pa_cvolume *cv)
574 {
575         uint32_t idx;
576         int rc;
577
578         if (dir == PLAY) {
579                 idx = pa_stream_get_index(stream[PLAY]);
580                 rc = EXEC_CONTEXT_OP(pa_context_set_sink_input_volume,
581                                      context, idx, cv);
582         } else {
583                 idx = pa_stream_get_device_index(stream[REC]);
584                 rc = EXEC_CONTEXT_OP(pa_context_set_source_volume_by_index,
585                                      context, idx, cv);
586         }
587         return rc;
588 }
589
590 static int chan_left_right(int ch)
591 {
592         if (!channel_map || channel_map->channels <= ch) {
593                 switch (ch) {
594                 case 0:
595                         return LEFT;
596                 case 1:
597                         return RIGHT;
598                 default:
599                         return -1;
600                 }
601         }
602
603         switch (channel_map->map[ch]) {
604         /*case PA_CHANNEL_POSITION_LEFT:*/      /* same as FRONT_LEFT */
605         case PA_CHANNEL_POSITION_FRONT_LEFT:
606         case PA_CHANNEL_POSITION_REAR_LEFT:
607         case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
608         case PA_CHANNEL_POSITION_SIDE_LEFT:
609         case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
610         case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
611                 return LEFT;
612         /*case PA_CHANNEL_POSITION_RIGHT:*/     /* same as FRONT_RIGHT */
613         case PA_CHANNEL_POSITION_FRONT_RIGHT:
614         case PA_CHANNEL_POSITION_REAR_RIGHT:
615         case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
616         case PA_CHANNEL_POSITION_SIDE_RIGHT:
617         case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
618         case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
619                 return RIGHT;
620         default:
621                 return -1;
622         }
623 }
624
625 static int do_mixer(int dir, int *vol)
626 {
627         pa_cvolume cv;
628         unsigned lv, rv;
629         int i, rc;
630
631         if (vol[0] >= 0) {
632                 int avg;
633
634                 stored_oss_vol[dir][LEFT] = vol[LEFT];
635                 stored_oss_vol[dir][RIGHT] = vol[RIGHT];
636                 vol[LEFT] = vol[LEFT] * PA_VOLUME_NORM / 100;
637                 vol[RIGHT] = vol[RIGHT] * PA_VOLUME_NORM / 100;
638                 avg = (vol[LEFT] + vol[RIGHT]) / 2;
639
640                 pa_cvolume_mute(&cv, sample_spec.channels);
641
642                 for (i = 0; i < cv.channels; i++)
643                         switch (chan_left_right(i)) {
644                         case LEFT:      cv.values[i] = vol[LEFT];       break;
645                         case RIGHT:     cv.values[i] = vol[RIGHT];      break;
646                         default:        cv.values[i] = avg;             break;
647                         }
648
649                 rc = set_volume(dir, &cv);
650                 if (rc)
651                         return rc;
652         }
653
654         rc = get_volume(dir, &cv);
655         if (rc)
656                 return rc;
657
658         if (cv.channels == 1)
659                 lv = rv = pa_cvolume_avg(&cv);
660         else {
661                 unsigned lcnt = 0, rcnt = 0;
662
663                 for (i = 0, lv = 0, rv = 0; i < cv.channels; i++)
664                         switch (chan_left_right(i)) {
665                         case LEFT:      lv += cv.values[i];     lcnt++; break;
666                         case RIGHT:     rv += cv.values[i];     rcnt++; break;
667                         }
668
669                 if (lcnt)
670                         lv /= lcnt;
671                 if (rcnt)
672                         rv /= rcnt;
673         }
674
675         vol[LEFT] = lv * 100 / PA_VOLUME_NORM;
676         vol[RIGHT] = rv * 100 / PA_VOLUME_NORM;
677
678         return 0;
679 }
680
681 static ssize_t padsp_mixer(enum ossp_opcode opcode,
682                            void *carg, void *din, size_t din_sz,
683                            void *rarg, void *dout, size_t *dout_szp, int tfd)
684 {
685         struct ossp_mixer_arg *arg = carg;
686         int i, rc[2] = { };
687
688         if (prepare_streams())
689                 return -EIO;
690
691         for (i = 0; i < 2; i++)
692                 if (stream[i])
693                         rc[i] = do_mixer(i, arg->vol[i]);
694                 else
695                         memset(arg->vol[i], -1, sizeof(arg->vol[i]));
696
697         *(struct ossp_mixer_arg *)rarg = *arg;
698         return rc[0] ?: rc[1];
699 }
700
701 static void context_state_callback(pa_context *cxt, void *userdata)
702 {
703         pa_threaded_mainloop_signal(mainloop, 0);
704 }
705
706 static void context_subscribe_callback(pa_context *context,
707                                        pa_subscription_event_type_t type,
708                                        uint32_t idx, void *userdata)
709 {
710         struct ossp_notify event = { .magic = OSSP_NOTIFY_MAGIC,
711                                      .opcode = OSSP_NOTIFY_VOLCHG };
712         ssize_t ret;
713
714         if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) !=
715             PA_SUBSCRIPTION_EVENT_CHANGE)
716                 return;
717
718         ret = write(ossp_notify_fd, &event, sizeof(event));
719         if (ret != sizeof(event) && errno != EPIPE)
720                 warn_e(-errno, "write to notify_fd failed");
721 }
722
723 static ssize_t padsp_open(enum ossp_opcode opcode,
724                           void *carg, void *din, size_t din_sz,
725                           void *rarg, void *dout, size_t *dout_szp, int tfd)
726 {
727         struct ossp_dsp_open_arg *arg = carg;
728         char host_name[128] = "(unknown)", opener[128] = "(unknown)";
729         int state;
730
731         switch (arg->flags & O_ACCMODE) {
732         case O_WRONLY:
733                 stream_enabled[PLAY] = 1;
734                 break;
735         case O_RDONLY:
736                 stream_enabled[REC] = 1;
737                 break;
738         case O_RDWR:
739                 stream_enabled[PLAY] = 1;
740                 stream_enabled[REC] = 1;
741                 break;
742         default:
743                 assert(0);
744         }
745
746         /* determine stream name */
747         gethostname(host_name, sizeof(host_name) - 1);
748         snprintf(stream_name, sizeof(stream_name), "OSS Proxy %s/%s:%ld",
749                  host_name, ossp_user_name, (long)arg->opener_pid);
750
751         /* create and connect PA context */
752         get_proc_self_info(arg->opener_pid, NULL, opener, sizeof(opener));
753         context = pa_context_new(mainloop_api, opener);
754         if (!context) {
755                 err("pa_context_new() failed");
756                 return -EIO;
757         }
758
759         pa_context_set_state_callback(context, context_state_callback, NULL);
760         pa_context_set_subscribe_callback(context, context_subscribe_callback,
761                                           NULL);
762
763         pa_context_connect(context, NULL, 0, NULL);
764         while (1) {
765                 state = pa_context_get_state(context);
766                 if (state != PA_CONTEXT_CONNECTING &&
767                     state != PA_CONTEXT_AUTHORIZING &&
768                     state != PA_CONTEXT_SETTING_NAME)
769                         break;
770
771                 pa_threaded_mainloop_wait(mainloop);
772         }
773
774         if (EXEC_CONTEXT_OP(pa_context_subscribe, context,
775                             PA_SUBSCRIPTION_MASK_SINK_INPUT |
776                             PA_SUBSCRIPTION_MASK_SOURCE))
777                 warn_pa("failed to subscribe to context events");
778
779         if (state != PA_CONTEXT_READY) {
780                 err_pa("failed to connect context, state=%d", state);
781                 return -EIO;
782         }
783
784         return 0;
785 }
786
787 static void mmap_fill_pstg(void)
788 {
789         struct ring_buf *stg = &mmap_stg[PLAY];
790         struct ring_buf mmap;
791         uint64_t new_idx = get_mmap_idx(PLAY);
792         size_t bytes, space, size;
793         void *data;
794
795         if (new_idx <= mmap_idx[PLAY])
796                 return;
797
798         bytes = new_idx - mmap_idx[PLAY];
799         space = ring_space(stg);
800
801         if (bytes > mmap_size) {
802                 dbg0("mmap playback transfer chunk bigger than "
803                      "mmap size (bytes=%zu mmap_size=%zu)", bytes, mmap_size);
804                 mmap_sync[PLAY] = 1;
805                 bytes = mmap_size;
806         }
807
808         if (bytes > space) {
809                 dbg0("mmap playback staging buffer overflow "
810                      "(bytes=%zu space=%zu)", bytes, space);
811                 mmap_sync[PLAY] = 1;
812                 bytes = space;
813         }
814
815         ring_manual_init(&mmap, mmap_map[PLAY], mmap_size,
816                          new_idx % mmap_size, bytes);
817
818         while ((data = ring_data(&mmap, &size))) {
819                 ring_fill(stg, data, size);
820                 ring_consume(&mmap, size);
821         }
822
823         mmap_idx[PLAY] = new_idx;
824 }
825
826 static void mmap_consume_rstg(void)
827 {
828         struct ring_buf *stg = &mmap_stg[REC];
829         struct ring_buf mmap;
830         uint64_t new_idx = get_mmap_idx(REC);
831         uint64_t fill_idx = mmap_idx[REC];
832         size_t bytes, space;
833
834         if (new_idx <= mmap_idx[REC])
835                 return;
836
837         space = new_idx - mmap_idx[REC];        /* mmapped space to fill in */
838         bytes = ring_bytes(stg);                /* recorded bytes in staging */ 
839
840         if (space > bytes) {
841                 if (!mmap_sync[REC])
842                         dbg0("mmap recording staging buffer underflow "
843                              "(space=%zu bytes=%zu)", space, bytes);
844                 mmap_sync[REC] = 1;
845         }
846
847         if (space > mmap_size) {
848                 if (!mmap_sync[REC])
849                         dbg0("mmap recording transfer chunk bigger than "
850                              "mmap size (space=%zu mmap_size=%zu)",
851                              bytes, mmap_size);
852                 mmap_sync[REC] = 1;
853                 space = mmap_size;
854         }
855
856         /* If resync is requested, leave lead bytes in the staging
857          * buffer and copy everything else such that data is filled
858          * upto the new_idx.  If there are more bytes in staging than
859          * available space, those will be dropped.
860          */
861         if (mmap_sync[REC]) {
862                 ssize_t avail = bytes - mmap_lead[REC];
863
864                 /* make sure we always have lead bytes in staging */
865                 if (avail < 0)
866                         goto skip;
867
868                 if (avail > space) {
869                         dbg0("dropping %zu bytes from record staging buffer",
870                              avail - space);
871                         ring_consume(&mmap_stg[REC], avail - space);
872                         avail = space;
873                 } else {
874                         dbg0("skippping %zu bytes in record mmap map",
875                              space - avail);
876                         space = avail;
877                 }
878
879                 assert(new_idx >= avail);
880                 fill_idx = new_idx - avail;
881                 mmap_sync[REC] = 0;
882         }
883
884         ring_manual_init(&mmap, mmap_map[REC], mmap_size,
885                          fill_idx % mmap_size, 0);
886
887         while (space) {
888                 void *data;
889                 size_t size, todo;
890
891                 data = ring_data(stg, &size);
892                 assert(data);
893
894                 todo = min(size, space);
895                 ring_fill(&mmap, data, todo);
896
897                 ring_consume(stg, todo);
898                 space -= todo;
899         }
900
901  skip:
902         mmap_idx[REC] = new_idx;
903 }
904
905 static void do_mmap_write(size_t space)
906 {
907         struct ring_buf *stg = &mmap_stg[PLAY];
908         size_t todo;
909         void *data;
910
911         space = round_down(space, frame_size);
912         mmap_fill_pstg();
913
914         while (space && (data = ring_data(stg, &todo))) {
915                 pa_seek_mode_t mode = PA_SEEK_RELATIVE_END;
916                 int64_t offset = 0;
917
918                 todo = min(todo, space);
919
920                 if (mmap_sync[PLAY]) {
921                         mode = PA_SEEK_RELATIVE_ON_READ;
922                         offset = (int64_t)mmap_lead[PLAY] - ring_bytes(stg);
923                         dbg0("mmap resync, offset=%ld", (long)offset);
924                 }
925
926                 if (pa_stream_write(stream[PLAY], data, todo, NULL,
927                                     offset, mode) < 0) {
928                         err_pa("pa_stream_write() failed");
929                         padsp_done();
930                         return;
931                 }
932
933                 mmap_sync[PLAY] = 0;
934                 ring_consume(stg, todo);
935                 space -= todo;
936         }
937 }
938
939 static void do_mmap_read(size_t bytes)
940 {
941         struct ring_buf *stg = &mmap_stg[REC];
942
943         bytes = round_down(bytes, frame_size);
944         mmap_consume_rstg();
945
946         while (bytes) {
947                 const void *peek_data;
948                 size_t size;
949
950                 if (pa_stream_peek(stream[REC], &peek_data, &size)) {
951                         err_pa("pa_stream_peek() failed");
952                         padsp_done();
953                         return;
954                 }
955
956                 if (!peek_data)
957                         break;
958
959                 if (size <= ring_space(stg))
960                         ring_fill(stg, peek_data, size);
961                 else {
962                         if (!mmap_sync[REC])
963                                 dbg0("recording staging buffer overflow, "
964                                      "requesting resync");
965                         mmap_sync[REC] = 1;
966                 }
967
968                 pa_stream_drop(stream[REC]);
969                 bytes -= size;
970         }
971 }
972
973 static void stream_rw_callback(pa_stream *s, size_t length, void *userdata)
974 {
975         int dir;
976         size_t size;
977
978         if (s == stream[PLAY]) {
979                 dir = PLAY;
980                 size = pa_stream_writable_size(s);
981                 if (mmap_map[PLAY])
982                         do_mmap_write(size);
983         } else if (s == stream[REC]) {
984                 dir = REC;
985                 size = pa_stream_readable_size(s);
986                 if (mmap_map[REC])
987                         do_mmap_read(size);
988         } else {
989                 dbg0("stream_rw_callback(): unknown stream %p PLAY/REC=%p/%p\n",
990                      s, stream[PLAY], stream[REC]);
991                 return;
992         }
993
994         if (size < user_frag_size)
995                 return;
996         if (stream_waiting)
997                 pa_threaded_mainloop_signal(mainloop, 0);
998         if (stream_notify) {
999                 struct ossp_notify event = { .magic = OSSP_NOTIFY_MAGIC,
1000                                              .opcode = OSSP_NOTIFY_POLL };
1001                 ssize_t ret;
1002
1003                 ret = write(ossp_notify_fd, &event, sizeof(event));
1004                 if (ret != sizeof(event)) {
1005                         if (errno != EPIPE)
1006                                 err_e(-errno, "write to notify_fd failed");
1007
1008                         /* This function is run from PA mainloop and
1009                          * thus the following padsp_done() won't be
1010                          * noticed before the mainthread tries to run
1011                          * the next command.  Well, that's good enough.
1012                          */
1013                         padsp_done();
1014                 }
1015                 stream_notify = 0;
1016         }
1017 }
1018
1019 static ssize_t padsp_write(enum ossp_opcode opcode,
1020                            void *carg, void *din, size_t din_sz,
1021                            void *rarg, void *dout, size_t *dout_szp, int tfd)
1022 {
1023         struct ossp_dsp_rw_arg *arg = carg;
1024         size_t size;
1025
1026         if (prepare_streams() || !stream[PLAY])
1027                 return -EIO;
1028
1029         stream_waiting++;
1030         while (1) {
1031                 size = pa_stream_writable_size(stream[PLAY]);
1032                 if (arg->nonblock || size >= user_frag_size)
1033                         break;
1034                 pa_threaded_mainloop_wait(mainloop);
1035         }
1036         stream_waiting--;
1037
1038         size = round_down(size, user_frag_size);
1039         if (!size)
1040                 return -EAGAIN;
1041
1042         size = min(size, din_sz);
1043
1044         if (pa_stream_write(stream[PLAY], din, size, NULL,
1045                             0, PA_SEEK_RELATIVE) < 0) {
1046                 err_pa("pa_stream_write() failed");
1047                 return padsp_done();
1048         }
1049
1050         return size;
1051 }
1052
1053 static ssize_t padsp_read(enum ossp_opcode opcode,
1054                           void *carg, void *din, size_t din_sz,
1055                           void *rarg, void *dout, size_t *dout_szp, int tfd)
1056 {
1057         struct ossp_dsp_rw_arg *arg = carg;
1058         size_t size;
1059         void *data;
1060
1061         if (prepare_streams() || !stream[REC])
1062                 return -EIO;
1063  again:
1064         if (!arg->nonblock) {
1065                 stream_waiting++;
1066                 while (1) {
1067                         size = pa_stream_readable_size(stream[REC]);
1068                         if (size + ring_bytes(&rec_buf) >= user_frag_size)
1069                                 break;
1070                         pa_threaded_mainloop_wait(mainloop);
1071                 }
1072                 stream_waiting--;
1073         }
1074
1075         while (ring_bytes(&rec_buf) < max(user_frag_size, *dout_szp)) {
1076                 const void *peek_data;
1077
1078                 if (pa_stream_peek(stream[REC], &peek_data, &size) < 0) {
1079                         err_pa("pa_stream_peek() failed");
1080                         return padsp_done();
1081                 }
1082
1083                 if (!peek_data)
1084                         break;
1085
1086                 if (ring_space(&rec_buf) < size) {
1087                         size_t bufsz;
1088
1089                         bufsz = ring_size(&rec_buf);
1090                         bufsz = max(2 * bufsz, bufsz + 2 * size);
1091
1092                         if (ring_resize(&rec_buf, bufsz)) {
1093                                 err("failed to allocate recording buffer");
1094                                 return padsp_done();
1095                         }
1096                 }
1097
1098                 ring_fill(&rec_buf, peek_data, size);
1099                 pa_stream_drop(stream[REC]);
1100         }
1101
1102         size = round_down(ring_bytes(&rec_buf), user_frag_size);
1103         if (!size) {
1104                 if (arg->nonblock)
1105                         return -EAGAIN;
1106                 else
1107                         goto again;
1108         }
1109
1110         *dout_szp = size = min(size, *dout_szp);
1111
1112         while (size) {
1113                 size_t cnt;
1114
1115                 data = ring_data(&rec_buf, &cnt);
1116                 assert(data);
1117
1118                 cnt = min(size, cnt);
1119                 memcpy(dout, data, cnt);
1120                 ring_consume(&rec_buf, cnt);
1121                 dout += cnt;
1122                 size -= cnt;
1123         }
1124
1125         return *dout_szp;
1126 }
1127
1128 static ssize_t padsp_poll(enum ossp_opcode opcode,
1129                           void *carg, void *din, size_t din_sz,
1130                           void *rarg, void *dout, size_t *dout_szp, int tfd)
1131 {
1132         unsigned revents = 0;
1133
1134         if (prepare_streams() < 0)
1135                 return -EIO;
1136
1137         stream_notify |= *(int *)carg;
1138
1139         if (stream[PLAY] &&
1140             pa_stream_writable_size(stream[PLAY]) >= user_frag_size)
1141                 revents |= POLLOUT;
1142         if (stream[REC] &&
1143             pa_stream_readable_size(stream[REC]) >= user_frag_size)
1144                 revents |= POLLIN;
1145
1146         *(unsigned *)rarg = revents;
1147         return 0;
1148 }
1149
1150 static ssize_t padsp_mmap(enum ossp_opcode opcode,
1151                           void *carg, void *din, size_t din_sz,
1152                           void *rarg, void *dout, size_t *dout_szp, int tfd)
1153 {
1154         struct ossp_dsp_mmap_arg *arg = carg;
1155         int dir = arg->dir;
1156
1157         assert(!mmap_map[dir]);
1158
1159         kill_streams();
1160
1161         /* arg->size is rounded up to the nearest page boundary.
1162          * There is no way to tell what the actual requested value is
1163          * but assume that it was the reported buffer space if it
1164          * falls into the same page aligned range.
1165          */
1166         mmap_raw_size = arg->size;
1167         if (user_max_length && user_max_length < mmap_raw_size &&
1168             round_up(mmap_raw_size, page_size) ==
1169             round_up(user_max_length, page_size)) {
1170                 info("MMAP adjusting raw_size %zu -> %zu",
1171                      mmap_raw_size, user_max_length);
1172                 mmap_raw_size = user_max_length;
1173         }
1174
1175         dbg0("MMAP server-addr=%p sz=%zu", ossp_mmap_addr[dir], mmap_raw_size);
1176
1177         mmap_map[dir] = ossp_mmap_addr[dir];
1178
1179         /* if mmapped, only mmapped streams are enabled */
1180         stream_enabled[PLAY] = !!mmap_map[PLAY];
1181         stream_enabled[REC] = !!mmap_map[REC];
1182
1183         return 0;
1184 }
1185
1186 static ssize_t padsp_munmap(enum ossp_opcode opcode,
1187                             void *carg, void *din, size_t din_sz,
1188                             void *rarg, void *dout, size_t *dout_szp, int tfd)
1189 {
1190         int dir = *(int *)carg;
1191
1192         assert(mmap_map[dir]);
1193         kill_streams();
1194         mmap_map[dir] = NULL;
1195         return 0;
1196 }
1197
1198 static ssize_t padsp_flush(enum ossp_opcode opcode,
1199                            void *carg, void *din, size_t din_sz,
1200                            void *rarg, void *dout, size_t *dout_szp, int tfd)
1201 {
1202         flush_streams(opcode == OSSP_DSP_SYNC);
1203         return 0;
1204 }
1205
1206 static ssize_t padsp_post(enum ossp_opcode opcode,
1207                           void *carg, void *din, size_t din_sz,
1208                           void *rarg, void *dout, size_t *dout_szp, int tfd)
1209 {
1210         return trigger_streams(1, -1);
1211 }
1212
1213 static ssize_t padsp_get_param(enum ossp_opcode opcode,
1214                                void *carg, void *din, size_t din_sz,
1215                                void *rarg, void *dout, size_t *dout_szp,
1216                                int tfd)
1217 {
1218         int v = 0;
1219
1220         switch (opcode) {
1221         case OSSP_DSP_GET_RATE:
1222                 v = sample_spec.rate;
1223                 break;
1224
1225         case OSSP_DSP_GET_CHANNELS:
1226                 v = sample_spec.channels;
1227                 break;
1228
1229         case OSSP_DSP_GET_FORMAT:
1230                 v = fmt_pa_to_oss(sample_spec.format);
1231                 break;
1232
1233         case OSSP_DSP_GET_BLKSIZE:
1234                 if (prepare_streams() < 0)
1235                         return -EIO;
1236                 v = user_frag_size;
1237                 break;
1238
1239         case OSSP_DSP_GET_FORMATS:
1240                 v = AFMT_U8 | AFMT_A_LAW | AFMT_MU_LAW | AFMT_S16_LE |
1241                         AFMT_S16_BE | AFMT_FLOAT | AFMT_S32_LE | AFMT_S32_BE;
1242                 break;
1243
1244         case OSSP_DSP_GET_TRIGGER:
1245                 if (!stream_corked[PLAY])
1246                         v |= PCM_ENABLE_OUTPUT;
1247                 if (!stream_corked[REC])
1248                         v |= PCM_ENABLE_INPUT;
1249                 break;
1250
1251         default:
1252                 assert(0);
1253         }
1254
1255         *(int *)rarg = v;
1256
1257         return 0;
1258 }
1259
1260 static ssize_t padsp_set_param(enum ossp_opcode opcode,
1261                                void *carg, void *din, size_t din_sz,
1262                                void *rarg, void *dout, size_t *dout_szp,
1263                                int tfd)
1264 {
1265         pa_sample_spec new_spec = sample_spec;
1266         int v = *(int *)carg;
1267
1268         /* kill the streams before changing parameters */
1269         kill_streams();
1270
1271         switch (opcode) {
1272         case OSSP_DSP_SET_RATE:
1273                 new_spec.rate = v;
1274                 if (pa_sample_spec_valid(&new_spec))
1275                         sample_spec = new_spec;
1276                 v = sample_spec.rate;
1277                 break;
1278
1279         case OSSP_DSP_SET_CHANNELS:
1280                 new_spec.channels = v;
1281                 if (pa_sample_spec_valid(&new_spec))
1282                         sample_spec = new_spec;
1283                 v = sample_spec.channels;
1284                 break;
1285
1286         case OSSP_DSP_SET_FORMAT:
1287                 new_spec.format = fmt_oss_to_pa(v);
1288                 if (pa_sample_spec_valid(&new_spec))
1289                         sample_spec = new_spec;
1290                 v = fmt_pa_to_oss(sample_spec.format);
1291                 break;
1292
1293         case OSSP_DSP_SET_SUBDIVISION:
1294                 if (!v) {
1295                         v = user_subdivision ?: 1;
1296                         break;
1297                 }
1298                 user_frag_size= 0;
1299                 user_subdivision = v;
1300                 break;
1301
1302         case OSSP_DSP_SET_FRAGMENT:
1303                 user_subdivision = 0;
1304                 user_frag_size = 1 << (v & 0xffff);
1305                 user_max_frags = (v >> 16) & 0xffff;
1306                 if (user_frag_size < 4)
1307                         user_frag_size = 4;
1308                 if (user_max_frags < 2)
1309                         user_max_frags = 2;
1310                 break;
1311         default:
1312                 assert(0);
1313         }
1314
1315         if (rarg)
1316                 *(int *)rarg = v;
1317         return 0;
1318 }
1319
1320 static ssize_t padsp_set_trigger(enum ossp_opcode opcode,
1321                                  void *carg, void *din, size_t din_sz,
1322                                  void *rarg, void *dout, size_t *dout_szp,
1323                                  int fd)
1324 {
1325         int enable = *(int *)carg;
1326
1327         return trigger_streams(enable & PCM_ENABLE_OUTPUT,
1328                                enable & PCM_ENABLE_INPUT);
1329 }
1330
1331 static ssize_t padsp_get_space(enum ossp_opcode opcode,
1332                                void *carg, void *din, size_t din_sz,
1333                                void *rarg, void *dout, size_t *dout_szp, int tfd)
1334 {
1335         int dir = (opcode == OSSP_DSP_GET_OSPACE) ? PLAY : REC;
1336         struct audio_buf_info info = { };
1337         int rc;
1338
1339         rc = prepare_streams();
1340         if (rc)
1341                 return -EIO;
1342
1343         if (mmapped()) {
1344                 info.fragments = mmap_raw_size / user_frag_size;
1345                 info.fragstotal = info.fragments;
1346                 info.fragsize = user_frag_size;
1347                 info.bytes = mmap_raw_size;
1348         } else {
1349                 size_t space;
1350
1351                 if (dir == PLAY)
1352                         space = pa_stream_writable_size(stream[PLAY]);
1353                 else
1354                         space = pa_stream_readable_size(stream[REC]);
1355
1356                 space = round_down(space, user_frag_size);
1357                 space = min(space, user_frag_size * user_max_frags);
1358
1359                 info.fragments = space / user_frag_size;
1360                 info.fragstotal = user_max_frags;
1361                 info.fragsize = user_frag_size;
1362                 info.bytes = space;
1363         }
1364
1365         *(struct audio_buf_info *)rarg = info;
1366         return 0;
1367 }
1368
1369 static ssize_t padsp_get_ptr(enum ossp_opcode opcode,
1370                              void *carg, void *din, size_t din_sz,
1371                              void *rarg, void *dout, size_t *dout_szp, int tfd)
1372 {
1373         int dir = (opcode == OSSP_DSP_GET_OPTR) ? PLAY : REC;
1374         struct count_info info = { };
1375
1376         if (prepare_streams() < 0 || !stream[dir])
1377                 return -EIO;
1378
1379         if (mmap_map[dir]) {
1380                 /* mmap operation in progress, report mmap buffer parameters */
1381                 if (dir == PLAY)
1382                         mmap_fill_pstg();
1383                 else
1384                         mmap_consume_rstg();
1385
1386                 info.bytes = mmap_idx[dir];
1387                 info.blocks = (mmap_idx[dir] - mmap_last_idx[dir]) / frame_size;
1388                 info.ptr = mmap_idx[dir] % mmap_size;
1389
1390                 mmap_last_idx[dir] = mmap_idx[dir];
1391         } else {
1392                 /* simulate pointers using timestamps */
1393                 double bpus = (double)sample_bps / 1000000;
1394                 size_t bytes, delta_bytes;
1395                 pa_usec_t usec, delta;
1396
1397                 if (pa_stream_get_time(stream[dir], &usec) < 0) {
1398                         warn_pa("pa_stream_get_time() failed");
1399                         return -EIO;
1400                 }
1401
1402                 delta = usec - stream_ptr_timestamp[dir];
1403                 stream_ptr_timestamp[dir] = usec;
1404                 bytes = bpus * usec;
1405                 delta_bytes = bpus * delta;
1406
1407                 info.bytes = bytes & INT_MAX;
1408                 info.blocks = (delta_bytes + frame_size - 1) / frame_size;
1409                 info.ptr = bytes % user_max_length;
1410         }
1411
1412         *(struct count_info *)rarg = info;
1413         return 0;
1414 }
1415
1416 static ssize_t padsp_get_odelay(enum ossp_opcode opcode,
1417                                 void *carg, void *din, size_t din_sz,
1418                                 void *rarg, void *dout, size_t *dout_szp,
1419                                 int fd)
1420 {
1421         double bpus = (double)sample_bps / 1000000;
1422         pa_usec_t usec;
1423
1424         if (prepare_streams() < 0 || !stream[PLAY])
1425                 return -EIO;
1426
1427         if (pa_stream_get_latency(stream[PLAY], &usec, NULL) < 0) {
1428                 warn_pa("pa_stream_get_latency() failed");
1429                 return -EIO;
1430         }
1431
1432         *(int *)rarg = bpus * usec;
1433         return 0;
1434 }
1435
1436 static ossp_action_fn_t action_fn_tbl[OSSP_NR_OPCODES] = {
1437         [OSSP_MIXER]            = padsp_mixer,
1438         [OSSP_DSP_OPEN]         = padsp_open,
1439         [OSSP_DSP_READ]         = padsp_read,
1440         [OSSP_DSP_WRITE]        = padsp_write,
1441         [OSSP_DSP_POLL]         = padsp_poll,
1442         [OSSP_DSP_MMAP]         = padsp_mmap,
1443         [OSSP_DSP_MUNMAP]       = padsp_munmap,
1444         [OSSP_DSP_RESET]        = padsp_flush,
1445         [OSSP_DSP_SYNC]         = padsp_flush,
1446         [OSSP_DSP_POST]         = padsp_post,
1447         [OSSP_DSP_GET_RATE]     = padsp_get_param,
1448         [OSSP_DSP_GET_CHANNELS] = padsp_get_param,
1449         [OSSP_DSP_GET_FORMAT]   = padsp_get_param,
1450         [OSSP_DSP_GET_BLKSIZE]  = padsp_get_param,
1451         [OSSP_DSP_GET_FORMATS]  = padsp_get_param,
1452         [OSSP_DSP_SET_RATE]     = padsp_set_param,
1453         [OSSP_DSP_SET_CHANNELS] = padsp_set_param,
1454         [OSSP_DSP_SET_FORMAT]   = padsp_set_param,
1455         [OSSP_DSP_SET_SUBDIVISION] = padsp_set_param,
1456         [OSSP_DSP_SET_FRAGMENT] = padsp_set_param,
1457         [OSSP_DSP_GET_TRIGGER]  = padsp_get_param,
1458         [OSSP_DSP_SET_TRIGGER]  = padsp_set_trigger,
1459         [OSSP_DSP_GET_OSPACE]   = padsp_get_space,
1460         [OSSP_DSP_GET_ISPACE]   = padsp_get_space,
1461         [OSSP_DSP_GET_OPTR]     = padsp_get_ptr,
1462         [OSSP_DSP_GET_IPTR]     = padsp_get_ptr,
1463         [OSSP_DSP_GET_ODELAY]   = padsp_get_odelay,
1464 };
1465
1466 static int action_pre(void)
1467 {
1468         pa_threaded_mainloop_lock(mainloop);
1469         if (fail_code) {
1470                 pa_threaded_mainloop_unlock(mainloop);
1471                 return fail_code;
1472         }
1473         return 0;
1474 }
1475
1476 static void action_post(void)
1477 {
1478         pa_threaded_mainloop_unlock(mainloop);
1479 }
1480
1481 int main(int argc, char **argv)
1482 {
1483         int rc;
1484
1485         ossp_slave_init(argc, argv);
1486
1487         page_size = sysconf(_SC_PAGE_SIZE);
1488
1489         mainloop = pa_threaded_mainloop_new();
1490         if (!mainloop) {
1491                 err("failed to allocate mainloop");
1492                 return 1;
1493         }
1494         mainloop_api = pa_threaded_mainloop_get_api(mainloop);
1495
1496         if (pa_threaded_mainloop_start(mainloop)) {
1497                 err("pa_mainloop_start() failed");
1498                 return 1;
1499         }
1500
1501         /* Okay, now we're open for business */
1502         rc = 0;
1503         do {
1504                 rc = ossp_slave_process_command(ossp_cmd_fd, action_fn_tbl,
1505                                                 action_pre, action_post);
1506         } while (rc > 0 && !fail_code);
1507         if (rc)
1508                 fail_code = rc;
1509
1510         pa_threaded_mainloop_lock(mainloop);
1511
1512         kill_streams();
1513         if (context) {
1514                 pa_context_disconnect(context);
1515                 pa_context_unref(context);
1516         }
1517
1518         pa_threaded_mainloop_unlock(mainloop);
1519
1520         pa_threaded_mainloop_stop(mainloop);
1521         pa_threaded_mainloop_free(mainloop);
1522
1523         return fail_code ? 1 : 0;
1524 }