Implement mmap support with STORE and RETRIEVE notifications
[ossp.git] / ossp-padsp.c
blob083cbf1c79da417dfb4fc7c4f3493484210dbb8f
1 /*
2 * ossp-padsp - ossp DSP slave which forwards to pulseaduio
4 * Copyright (C) 2008-2010 SUSE Linux Products GmbH
5 * Copyright (C) 2008-2010 Tejun Heo <tj@kernel.org>
7 * This file is released under the GPLv2.
8 */
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>
26 #include <pulse/pulseaudio.h>
27 #include <sys/soundcard.h>
29 #include "ossp-slave.h"
31 enum {
32 AFMT_FLOAT = 0x00004000,
33 AFMT_S32_LE = 0x00001000,
34 AFMT_S32_BE = 0x00002000,
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;
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 },
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;
78 static pa_sample_spec sample_spec = {
79 .format = PA_SAMPLE_U8,
80 .rate = 8000,
81 .channels = 1,
83 static size_t sample_bps = 8000;
84 static size_t frame_size = 1;
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;
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;
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 */
107 static const char *dir_str[] = {
108 [PLAY] = "PLAY",
109 [REC] = "REC",
112 static void stream_rw_callback(pa_stream *s, size_t length, void *userdata);
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)
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))
125 static int do_mixer(int dir, int *vol);
127 static int padsp_done(void)
129 fail_code = -EIO;
130 mainloop_api->quit(mainloop_api, 1);
131 return fail_code;
134 static int fmt_oss_to_pa(int fmt)
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;
149 static int fmt_pa_to_oss(int fmt)
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;
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)
173 static void context_op_callback(pa_context *s, int success, void *userdata)
175 *(int *)userdata = success;
176 pa_threaded_mainloop_signal(mainloop, 0);
179 static void stream_op_callback(pa_stream *s, int success, void *userdata)
181 *(int *)userdata = success;
182 pa_threaded_mainloop_signal(mainloop, 0);
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; })
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; })
199 static int mmapped(void)
201 return mmap_map[PLAY] || mmap_map[REC];
204 static uint64_t get_mmap_idx(int dir)
206 uint64_t idx;
207 pa_usec_t time;
209 if (!stream[dir])
210 return mmap_idx[dir];
212 if (pa_stream_get_time(stream[dir], &time) < 0) {
213 dbg1_pa("pa_stream_get_time() failed");
214 return mmap_idx[dir];
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;
222 return idx;
225 static void flush_streams(int drain)
227 int i;
229 if (!(stream[PLAY] || stream[REC]))
230 return;
232 dbg0("FLUSH drain=%d", drain);
234 /* mmapped streams run forever, can't drain */
235 if (drain && !mmapped() && stream[PLAY])
236 EXEC_STREAM_OP(pa_stream_drain, stream[PLAY]);
238 for (i = 0; i < 2; i++)
239 if (stream[i])
240 EXEC_STREAM_OP(pa_stream_flush, stream[i]);
242 ring_consume(&rec_buf, ring_bytes(&rec_buf));
245 static void kill_streams(void)
247 int dir;
249 if (!(stream[PLAY] || stream[REC]))
250 return;
252 flush_streams(1);
254 dbg0("KILL");
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;
264 ring_consume(&mmap_stg[dir], ring_bytes(&mmap_stg[dir]));
265 ring_resize(&mmap_stg[dir], 0);
269 static int trigger_streams(int play, int rec)
271 int ret = 0, dir, rc;
273 if (play >= 0)
274 stream_corked[PLAY] = !play;
275 if (rec >= 0)
276 stream_corked[REC] = !rec;
278 for (dir = 0; dir < 2; dir++) {
279 if (!stream[dir])
280 continue;
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;
290 return ret;
293 static void stream_state_callback(pa_stream *s, void *userdata)
295 pa_threaded_mainloop_signal(mainloop, 0);
298 static void stream_underflow_callback(pa_stream *s, void *userdata)
300 int dir = (s == stream[PLAY]) ? PLAY : REC;
302 dbg0("%s stream underrun", dir_str[dir]);
305 static void stream_overflow_callback(pa_stream *s, void *userdata)
307 int dir = (s == stream[PLAY]) ? PLAY : REC;
309 dbg0("%s stream overrun", dir_str[dir]);
312 static size_t duration_to_bytes(size_t dur)
314 return round_up(dur * sample_bps / 1000, frame_size);
317 static int prepare_streams(void)
319 const struct stream_params *sp;
320 size_t min_frag_size, min_target_length, tmp;
321 int dir, rc;
323 /* nothing to do? */
324 if ((!stream_enabled[PLAY] || stream[PLAY]) &&
325 (!stream_enabled[REC] || stream[REC]))
326 return 0;
328 /* determine sample parameters */
329 sample_bps = pa_bytes_per_second(&sample_spec);
330 frame_size = pa_frame_size(&sample_spec);
332 sp = &stream_params[PLAY];
333 if (stream_enabled[REC])
334 sp = &stream_params[REC];
336 min_frag_size = duration_to_bytes(sp->min_process);
337 min_target_length = duration_to_bytes(sp->min_latency);
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);
346 if (user_subdivision)
347 user_frag_size = round_up(sample_bps / user_subdivision,
348 frame_size);
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;
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;
366 user_frag_size = frag_size;
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;
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.
383 if (is_power2(frag_size)) {
384 target_length = frag_size;
385 while (target_length < max(tmp, page_size))
386 target_length <<= 1;
388 user_max_frags = target_length / frag_size;
391 user_max_length = user_frag_size * user_max_frags;
392 max_length = target_length + 2 * frag_size;
394 /* If mmapped, create backend stream with fixed parameters to
395 * create illusion of hardware buffer with acceptable latency.
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;
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);
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;
422 if (!stream_enabled[dir] || stream[dir])
423 continue;
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);
436 channel_map = pa_channel_map_init_auto(&channel_map_stor,
437 sample_spec.channels,
438 PA_CHANNEL_MAP_OSS);
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;
446 stream[dir] = s;
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);
461 flags = PA_STREAM_AUTO_TIMING_UPDATE |
462 PA_STREAM_INTERPOLATE_TIMING;
463 if (stream_corked[dir])
464 flags |= PA_STREAM_START_CORKED;
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;
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;
478 } else {
479 if (pa_stream_connect_record(s, NULL, &new_ba, flags)) {
480 err_pa("failed to connect record stream");
481 goto fail;
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;
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");
498 /* stream is ready setup mmap stuff */
499 if (!mmap_map[dir])
500 continue;
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;
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;
514 /* apply the current trigger settings */
515 trigger_streams(-1, -1);
518 return 0;
519 fail:
520 return padsp_done();
523 struct volume_ret {
524 int success;
525 pa_cvolume *cv;
528 static void play_volume_callback(pa_context *c, const pa_sink_input_info *i,
529 int eol, void *userdata)
531 struct volume_ret *vr = userdata;
533 if (i) {
534 *vr->cv = i->volume;
535 vr->success = 1;
537 pa_threaded_mainloop_signal(mainloop, 0);
540 static void rec_volume_callback(pa_context *c, const pa_source_info *i,
541 int eol, void *userdata)
543 struct volume_ret *vr = userdata;
545 if (i) {
546 *vr->cv = i->volume;
547 vr->success = 1;
549 pa_threaded_mainloop_signal(mainloop, 0);
552 static int get_volume(int dir, pa_cvolume *cv)
554 struct volume_ret vr = { .cv = cv };
555 uint32_t idx;
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);
566 if (!vr.success) {
567 warn_pa("failed to get %s volume", dir_str[dir]);
568 return -EIO;
570 return 0;
573 static int set_volume(int dir, pa_cvolume *cv)
575 uint32_t idx;
576 int rc;
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);
587 return rc;
590 static int chan_left_right(int ch)
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;
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;
625 static int do_mixer(int dir, int *vol)
627 pa_cvolume cv;
628 unsigned lv, rv;
629 int i, rc;
631 if (vol[0] >= 0) {
632 int avg;
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;
640 pa_cvolume_mute(&cv, sample_spec.channels);
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;
649 rc = set_volume(dir, &cv);
650 if (rc)
651 return rc;
654 rc = get_volume(dir, &cv);
655 if (rc)
656 return rc;
658 if (cv.channels == 1)
659 lv = rv = pa_cvolume_avg(&cv);
660 else {
661 unsigned lcnt = 0, rcnt = 0;
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;
669 if (lcnt)
670 lv /= lcnt;
671 if (rcnt)
672 rv /= rcnt;
675 vol[LEFT] = lv * 100 / PA_VOLUME_NORM;
676 vol[RIGHT] = rv * 100 / PA_VOLUME_NORM;
678 return 0;
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)
685 struct ossp_mixer_arg *arg = carg;
686 int i, rc[2] = { };
688 if (prepare_streams())
689 return -EIO;
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]));
697 *(struct ossp_mixer_arg *)rarg = *arg;
698 return rc[0] ?: rc[1];
701 static void context_state_callback(pa_context *cxt, void *userdata)
703 pa_threaded_mainloop_signal(mainloop, 0);
706 static void context_subscribe_callback(pa_context *context,
707 pa_subscription_event_type_t type,
708 uint32_t idx, void *userdata)
710 struct ossp_notify event = { .magic = OSSP_NOTIFY_MAGIC,
711 .opcode = OSSP_NOTIFY_VOLCHG };
712 ssize_t ret;
714 if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) !=
715 PA_SUBSCRIPTION_EVENT_CHANGE)
716 return;
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");
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)
727 struct ossp_dsp_open_arg *arg = carg;
728 char host_name[128] = "(unknown)", opener[128] = "(unknown)";
729 int state;
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);
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);
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;
759 pa_context_set_state_callback(context, context_state_callback, NULL);
760 pa_context_set_subscribe_callback(context, context_subscribe_callback,
761 NULL);
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;
771 pa_threaded_mainloop_wait(mainloop);
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");
779 if (state != PA_CONTEXT_READY) {
780 err_pa("failed to connect context, state=%d", state);
781 return -EIO;
784 return 0;
787 static void notify_mmap_fill_play(size_t mmap_size, size_t pos, size_t bytes)
789 while (bytes) {
790 ssize_t ret;
791 size_t count = min(bytes, mmap_size - pos);
792 struct ossp_notify event = { .magic = OSSP_NOTIFY_MAGIC,
793 .opcode = OSSP_NOTIFY_FILL };
795 ossp_mmap_transfer[PLAY].pos = pos;
796 ossp_mmap_transfer[PLAY].bytes = count;
798 ret = write(ossp_notify_fd, &event, sizeof(event));
799 if (ret != sizeof(event)) {
800 if (errno != EPIPE)
801 err_e(-errno, "write to notify_fd failed");
803 padsp_done();
804 return;
806 sem_wait(&ossp_mmap_transfer[PLAY].sem);
808 bytes -= count;
809 pos = (pos + count) % mmap_size;
813 static void notify_mmap_store_rec(size_t mmap_size, size_t pos, size_t bytes)
815 while (bytes) {
816 ssize_t ret;
817 size_t count = min(bytes, mmap_size - pos);
818 struct ossp_notify event = { .magic = OSSP_NOTIFY_MAGIC,
819 .opcode = OSSP_NOTIFY_STORE };
821 ossp_mmap_transfer[REC].pos = pos;
822 ossp_mmap_transfer[REC].bytes = count;
824 ret = write(ossp_notify_fd, &event, sizeof(event));
825 if (ret != sizeof(event)) {
826 if (errno != EPIPE)
827 err_e(-errno, "write to notify_fd failed");
829 padsp_done();
830 return;
832 sem_wait(&ossp_mmap_transfer[REC].sem);
834 bytes -= count;
835 pos = (pos + count) % mmap_size;
839 static void mmap_fill_pstg(void)
841 struct ring_buf *stg = &mmap_stg[PLAY];
842 struct ring_buf mmap;
843 uint64_t new_idx = get_mmap_idx(PLAY);
844 size_t bytes, space, size;
845 void *data;
847 if (new_idx <= mmap_idx[PLAY])
848 return;
850 bytes = new_idx - mmap_idx[PLAY];
851 space = ring_space(stg);
853 if (bytes > mmap_size) {
854 dbg0("mmap playback transfer chunk bigger than "
855 "mmap size (bytes=%zu mmap_size=%zu)", bytes, mmap_size);
856 mmap_sync[PLAY] = 1;
857 bytes = mmap_size;
860 if (bytes > space) {
861 dbg0("mmap playback staging buffer overflow "
862 "(bytes=%zu space=%zu)", bytes, space);
863 mmap_sync[PLAY] = 1;
864 bytes = space;
867 notify_mmap_fill_play(mmap_size, mmap_idx[PLAY] % mmap_size, bytes);
869 ring_manual_init(&mmap, mmap_map[PLAY], mmap_size,
870 new_idx % mmap_size, bytes);
872 while ((data = ring_data(&mmap, &size))) {
873 ring_fill(stg, data, size);
874 ring_consume(&mmap, size);
877 mmap_idx[PLAY] = new_idx;
880 static void mmap_consume_rstg(void)
882 struct ring_buf *stg = &mmap_stg[REC];
883 struct ring_buf mmap;
884 uint64_t new_idx = get_mmap_idx(REC);
885 uint64_t fill_idx = mmap_idx[REC];
886 size_t bytes, space;
888 if (new_idx <= mmap_idx[REC])
889 return;
891 space = new_idx - mmap_idx[REC]; /* mmapped space to fill in */
892 bytes = ring_bytes(stg); /* recorded bytes in staging */
894 if (space > bytes) {
895 if (!mmap_sync[REC])
896 dbg0("mmap recording staging buffer underflow "
897 "(space=%zu bytes=%zu)", space, bytes);
898 mmap_sync[REC] = 1;
901 if (space > mmap_size) {
902 if (!mmap_sync[REC])
903 dbg0("mmap recording transfer chunk bigger than "
904 "mmap size (space=%zu mmap_size=%zu)",
905 bytes, mmap_size);
906 mmap_sync[REC] = 1;
907 space = mmap_size;
910 /* If resync is requested, leave lead bytes in the staging
911 * buffer and copy everything else such that data is filled
912 * upto the new_idx. If there are more bytes in staging than
913 * available space, those will be dropped.
915 if (mmap_sync[REC]) {
916 ssize_t avail = bytes - mmap_lead[REC];
918 /* make sure we always have lead bytes in staging */
919 if (avail < 0)
920 goto skip;
922 if (avail > space) {
923 dbg0("dropping %zu bytes from record staging buffer",
924 avail - space);
925 ring_consume(&mmap_stg[REC], avail - space);
926 avail = space;
927 } else {
928 dbg0("skippping %zu bytes in record mmap map",
929 space - avail);
930 space = avail;
933 assert(new_idx >= avail);
934 fill_idx = new_idx - avail;
935 mmap_sync[REC] = 0;
938 ring_manual_init(&mmap, mmap_map[REC], mmap_size,
939 fill_idx % mmap_size, 0);
941 while (space) {
942 void *data;
943 size_t size, todo;
945 data = ring_data(stg, &size);
946 assert(data);
948 todo = min(size, space);
949 ring_fill(&mmap, data, todo);
951 ring_consume(stg, todo);
952 space -= todo;
955 notify_mmap_store_rec(mmap_size, fill_idx % mmap_size, ring_bytes(&mmap));
957 skip:
958 mmap_idx[REC] = new_idx;
961 static void do_mmap_write(size_t space)
963 struct ring_buf *stg = &mmap_stg[PLAY];
964 size_t todo;
965 void *data;
967 space = round_down(space, frame_size);
968 mmap_fill_pstg();
970 while (space && (data = ring_data(stg, &todo))) {
971 pa_seek_mode_t mode = PA_SEEK_RELATIVE_END;
972 int64_t offset = 0;
974 todo = min(todo, space);
976 if (mmap_sync[PLAY]) {
977 mode = PA_SEEK_RELATIVE_ON_READ;
978 offset = (int64_t)mmap_lead[PLAY] - ring_bytes(stg);
979 dbg0("mmap resync, offset=%ld", (long)offset);
982 if (pa_stream_write(stream[PLAY], data, todo, NULL,
983 offset, mode) < 0) {
984 err_pa("pa_stream_write() failed");
985 padsp_done();
986 return;
989 mmap_sync[PLAY] = 0;
990 ring_consume(stg, todo);
991 space -= todo;
995 static void do_mmap_read(size_t bytes)
997 struct ring_buf *stg = &mmap_stg[REC];
999 bytes = round_down(bytes, frame_size);
1000 mmap_consume_rstg();
1002 while (bytes) {
1003 const void *peek_data;
1004 size_t size;
1006 if (pa_stream_peek(stream[REC], &peek_data, &size)) {
1007 err_pa("pa_stream_peek() failed");
1008 padsp_done();
1009 return;
1012 if (!peek_data)
1013 break;
1015 if (size <= ring_space(stg))
1016 ring_fill(stg, peek_data, size);
1017 else {
1018 if (!mmap_sync[REC])
1019 dbg0("recording staging buffer overflow, "
1020 "requesting resync");
1021 mmap_sync[REC] = 1;
1024 pa_stream_drop(stream[REC]);
1025 bytes -= size;
1029 static void stream_rw_callback(pa_stream *s, size_t length, void *userdata)
1031 size_t size;
1033 if (s == stream[PLAY]) {
1034 size = pa_stream_writable_size(s);
1035 if (mmap_map[PLAY])
1036 do_mmap_write(size);
1037 } else if (s == stream[REC]) {
1038 size = pa_stream_readable_size(s);
1039 if (mmap_map[REC])
1040 do_mmap_read(size);
1041 } else {
1042 dbg0("stream_rw_callback(): unknown stream %p PLAY/REC=%p/%p\n",
1043 s, stream[PLAY], stream[REC]);
1044 return;
1047 if (size < user_frag_size)
1048 return;
1049 if (stream_waiting)
1050 pa_threaded_mainloop_signal(mainloop, 0);
1051 if (stream_notify) {
1052 struct ossp_notify event = { .magic = OSSP_NOTIFY_MAGIC,
1053 .opcode = OSSP_NOTIFY_POLL };
1054 ssize_t ret;
1056 ret = write(ossp_notify_fd, &event, sizeof(event));
1057 if (ret != sizeof(event)) {
1058 if (errno != EPIPE)
1059 err_e(-errno, "write to notify_fd failed");
1061 /* This function is run from PA mainloop and
1062 * thus the following padsp_done() won't be
1063 * noticed before the mainthread tries to run
1064 * the next command. Well, that's good enough.
1066 padsp_done();
1068 stream_notify = 0;
1072 static ssize_t padsp_write(enum ossp_opcode opcode,
1073 void *carg, void *din, size_t din_sz,
1074 void *rarg, void *dout, size_t *dout_szp, int tfd)
1076 struct ossp_dsp_rw_arg *arg = carg;
1077 size_t size;
1079 if (prepare_streams() || !stream[PLAY])
1080 return -EIO;
1082 stream_waiting++;
1083 while (1) {
1084 size = pa_stream_writable_size(stream[PLAY]);
1085 if (arg->nonblock || size >= user_frag_size)
1086 break;
1087 pa_threaded_mainloop_wait(mainloop);
1089 stream_waiting--;
1091 size = round_down(size, user_frag_size);
1092 if (!size)
1093 return -EAGAIN;
1095 size = min(size, din_sz);
1097 if (pa_stream_write(stream[PLAY], din, size, NULL,
1098 0, PA_SEEK_RELATIVE) < 0) {
1099 err_pa("pa_stream_write() failed");
1100 return padsp_done();
1103 return size;
1106 static ssize_t padsp_read(enum ossp_opcode opcode,
1107 void *carg, void *din, size_t din_sz,
1108 void *rarg, void *dout, size_t *dout_szp, int tfd)
1110 struct ossp_dsp_rw_arg *arg = carg;
1111 size_t size;
1112 void *data;
1114 if (prepare_streams() || !stream[REC])
1115 return -EIO;
1116 again:
1117 if (!arg->nonblock) {
1118 stream_waiting++;
1119 while (1) {
1120 size = pa_stream_readable_size(stream[REC]);
1121 if (size + ring_bytes(&rec_buf) >= user_frag_size)
1122 break;
1123 pa_threaded_mainloop_wait(mainloop);
1125 stream_waiting--;
1128 while (ring_bytes(&rec_buf) < max(user_frag_size, *dout_szp)) {
1129 const void *peek_data;
1131 if (pa_stream_peek(stream[REC], &peek_data, &size) < 0) {
1132 err_pa("pa_stream_peek() failed");
1133 return padsp_done();
1136 if (!peek_data)
1137 break;
1139 if (ring_space(&rec_buf) < size) {
1140 size_t bufsz;
1142 bufsz = ring_size(&rec_buf);
1143 bufsz = max(2 * bufsz, bufsz + 2 * size);
1145 if (ring_resize(&rec_buf, bufsz)) {
1146 err("failed to allocate recording buffer");
1147 return padsp_done();
1151 ring_fill(&rec_buf, peek_data, size);
1152 pa_stream_drop(stream[REC]);
1155 size = round_down(ring_bytes(&rec_buf), user_frag_size);
1156 if (!size) {
1157 if (arg->nonblock)
1158 return -EAGAIN;
1159 else
1160 goto again;
1163 *dout_szp = size = min(size, *dout_szp);
1165 while (size) {
1166 size_t cnt;
1168 data = ring_data(&rec_buf, &cnt);
1169 assert(data);
1171 cnt = min(size, cnt);
1172 memcpy(dout, data, cnt);
1173 ring_consume(&rec_buf, cnt);
1174 dout += cnt;
1175 size -= cnt;
1178 return *dout_szp;
1181 static ssize_t padsp_poll(enum ossp_opcode opcode,
1182 void *carg, void *din, size_t din_sz,
1183 void *rarg, void *dout, size_t *dout_szp, int tfd)
1185 unsigned revents = 0;
1187 if (prepare_streams() < 0)
1188 return -EIO;
1190 stream_notify |= *(int *)carg;
1192 if (stream[PLAY] &&
1193 pa_stream_writable_size(stream[PLAY]) >= user_frag_size)
1194 revents |= POLLOUT;
1195 if (stream[REC] &&
1196 pa_stream_readable_size(stream[REC]) >= user_frag_size)
1197 revents |= POLLIN;
1199 *(unsigned *)rarg = revents;
1200 return 0;
1203 static ssize_t padsp_mmap(enum ossp_opcode opcode,
1204 void *carg, void *din, size_t din_sz,
1205 void *rarg, void *dout, size_t *dout_szp, int tfd)
1207 struct ossp_dsp_mmap_arg *arg = carg;
1208 int dir = arg->dir;
1210 assert(!mmap_map[dir]);
1212 kill_streams();
1214 /* arg->size is rounded up to the nearest page boundary.
1215 * There is no way to tell what the actual requested value is
1216 * but assume that it was the reported buffer space if it
1217 * falls into the same page aligned range.
1219 mmap_raw_size = arg->size;
1220 if (user_max_length && user_max_length < mmap_raw_size &&
1221 round_up(mmap_raw_size, page_size) ==
1222 round_up(user_max_length, page_size)) {
1223 info("MMAP adjusting raw_size %zu -> %zu",
1224 mmap_raw_size, user_max_length);
1225 mmap_raw_size = user_max_length;
1228 dbg0("MMAP server-addr=%p sz=%zu", ossp_mmap_addr[dir], mmap_raw_size);
1230 mmap_map[dir] = ossp_mmap_addr[dir];
1232 /* if mmapped, only mmapped streams are enabled */
1233 stream_enabled[PLAY] = !!mmap_map[PLAY];
1234 stream_enabled[REC] = !!mmap_map[REC];
1236 return 0;
1239 static ssize_t padsp_munmap(enum ossp_opcode opcode,
1240 void *carg, void *din, size_t din_sz,
1241 void *rarg, void *dout, size_t *dout_szp, int tfd)
1243 int dir = *(int *)carg;
1245 assert(mmap_map[dir]);
1246 kill_streams();
1247 mmap_map[dir] = NULL;
1248 return 0;
1251 static ssize_t padsp_flush(enum ossp_opcode opcode,
1252 void *carg, void *din, size_t din_sz,
1253 void *rarg, void *dout, size_t *dout_szp, int tfd)
1255 flush_streams(opcode == OSSP_DSP_SYNC);
1256 return 0;
1259 static ssize_t padsp_post(enum ossp_opcode opcode,
1260 void *carg, void *din, size_t din_sz,
1261 void *rarg, void *dout, size_t *dout_szp, int tfd)
1263 return trigger_streams(1, -1);
1266 static ssize_t padsp_get_param(enum ossp_opcode opcode,
1267 void *carg, void *din, size_t din_sz,
1268 void *rarg, void *dout, size_t *dout_szp,
1269 int tfd)
1271 int v = 0;
1273 switch (opcode) {
1274 case OSSP_DSP_GET_RATE:
1275 v = sample_spec.rate;
1276 break;
1278 case OSSP_DSP_GET_CHANNELS:
1279 v = sample_spec.channels;
1280 break;
1282 case OSSP_DSP_GET_FORMAT:
1283 v = fmt_pa_to_oss(sample_spec.format);
1284 break;
1286 case OSSP_DSP_GET_BLKSIZE:
1287 if (prepare_streams() < 0)
1288 return -EIO;
1289 v = user_frag_size;
1290 break;
1292 case OSSP_DSP_GET_FORMATS:
1293 v = AFMT_U8 | AFMT_A_LAW | AFMT_MU_LAW | AFMT_S16_LE |
1294 AFMT_S16_BE | AFMT_FLOAT | AFMT_S32_LE | AFMT_S32_BE;
1295 break;
1297 case OSSP_DSP_GET_TRIGGER:
1298 if (!stream_corked[PLAY])
1299 v |= PCM_ENABLE_OUTPUT;
1300 if (!stream_corked[REC])
1301 v |= PCM_ENABLE_INPUT;
1302 break;
1304 default:
1305 assert(0);
1308 *(int *)rarg = v;
1310 return 0;
1313 static ssize_t padsp_set_param(enum ossp_opcode opcode,
1314 void *carg, void *din, size_t din_sz,
1315 void *rarg, void *dout, size_t *dout_szp,
1316 int tfd)
1318 pa_sample_spec new_spec = sample_spec;
1319 int v = *(int *)carg;
1321 /* kill the streams before changing parameters */
1322 kill_streams();
1324 switch (opcode) {
1325 case OSSP_DSP_SET_RATE:
1326 new_spec.rate = v;
1327 if (pa_sample_spec_valid(&new_spec))
1328 sample_spec = new_spec;
1329 v = sample_spec.rate;
1330 break;
1332 case OSSP_DSP_SET_CHANNELS:
1333 new_spec.channels = v;
1334 if (pa_sample_spec_valid(&new_spec))
1335 sample_spec = new_spec;
1336 v = sample_spec.channels;
1337 break;
1339 case OSSP_DSP_SET_FORMAT:
1340 new_spec.format = fmt_oss_to_pa(v);
1341 if (pa_sample_spec_valid(&new_spec))
1342 sample_spec = new_spec;
1343 v = fmt_pa_to_oss(sample_spec.format);
1344 break;
1346 case OSSP_DSP_SET_SUBDIVISION:
1347 if (!v) {
1348 v = user_subdivision ?: 1;
1349 break;
1351 user_frag_size= 0;
1352 user_subdivision = v;
1353 break;
1355 case OSSP_DSP_SET_FRAGMENT:
1356 user_subdivision = 0;
1357 user_frag_size = 1 << (v & 0xffff);
1358 user_max_frags = (v >> 16) & 0xffff;
1359 if (user_frag_size < 4)
1360 user_frag_size = 4;
1361 if (user_max_frags < 2)
1362 user_max_frags = 2;
1363 break;
1364 default:
1365 assert(0);
1368 if (rarg)
1369 *(int *)rarg = v;
1370 return 0;
1373 static ssize_t padsp_set_trigger(enum ossp_opcode opcode,
1374 void *carg, void *din, size_t din_sz,
1375 void *rarg, void *dout, size_t *dout_szp,
1376 int fd)
1378 int enable = *(int *)carg;
1380 return trigger_streams(enable & PCM_ENABLE_OUTPUT,
1381 enable & PCM_ENABLE_INPUT);
1384 static ssize_t padsp_get_space(enum ossp_opcode opcode,
1385 void *carg, void *din, size_t din_sz,
1386 void *rarg, void *dout, size_t *dout_szp, int tfd)
1388 int dir = (opcode == OSSP_DSP_GET_OSPACE) ? PLAY : REC;
1389 struct audio_buf_info info = { };
1390 int rc;
1392 rc = prepare_streams();
1393 if (rc)
1394 return -EIO;
1396 if (mmapped()) {
1397 info.fragments = mmap_raw_size / user_frag_size;
1398 info.fragstotal = info.fragments;
1399 info.fragsize = user_frag_size;
1400 info.bytes = mmap_raw_size;
1401 } else {
1402 size_t space;
1404 if (dir == PLAY)
1405 space = pa_stream_writable_size(stream[PLAY]);
1406 else
1407 space = pa_stream_readable_size(stream[REC]);
1409 space = round_down(space, user_frag_size);
1410 space = min(space, user_frag_size * user_max_frags);
1412 info.fragments = space / user_frag_size;
1413 info.fragstotal = user_max_frags;
1414 info.fragsize = user_frag_size;
1415 info.bytes = space;
1418 *(struct audio_buf_info *)rarg = info;
1419 return 0;
1422 static ssize_t padsp_get_ptr(enum ossp_opcode opcode,
1423 void *carg, void *din, size_t din_sz,
1424 void *rarg, void *dout, size_t *dout_szp, int tfd)
1426 int dir = (opcode == OSSP_DSP_GET_OPTR) ? PLAY : REC;
1427 struct count_info info = { };
1429 if (prepare_streams() < 0 || !stream[dir])
1430 return -EIO;
1432 if (mmap_map[dir]) {
1433 /* mmap operation in progress, report mmap buffer parameters */
1434 if (dir == PLAY)
1435 mmap_fill_pstg();
1436 else
1437 mmap_consume_rstg();
1439 info.bytes = mmap_idx[dir];
1440 info.blocks = (mmap_idx[dir] - mmap_last_idx[dir]) / frame_size;
1441 info.ptr = mmap_idx[dir] % mmap_size;
1443 mmap_last_idx[dir] = mmap_idx[dir];
1444 } else {
1445 /* simulate pointers using timestamps */
1446 double bpus = (double)sample_bps / 1000000;
1447 size_t bytes, delta_bytes;
1448 pa_usec_t usec, delta;
1450 if (pa_stream_get_time(stream[dir], &usec) < 0) {
1451 warn_pa("pa_stream_get_time() failed");
1452 return -EIO;
1455 delta = usec - stream_ptr_timestamp[dir];
1456 stream_ptr_timestamp[dir] = usec;
1457 bytes = bpus * usec;
1458 delta_bytes = bpus * delta;
1460 info.bytes = bytes & INT_MAX;
1461 info.blocks = (delta_bytes + frame_size - 1) / frame_size;
1462 info.ptr = bytes % user_max_length;
1465 *(struct count_info *)rarg = info;
1466 return 0;
1469 static ssize_t padsp_get_odelay(enum ossp_opcode opcode,
1470 void *carg, void *din, size_t din_sz,
1471 void *rarg, void *dout, size_t *dout_szp,
1472 int fd)
1474 double bpus = (double)sample_bps / 1000000;
1475 pa_usec_t usec;
1477 if (prepare_streams() < 0 || !stream[PLAY])
1478 return -EIO;
1480 if (pa_stream_get_latency(stream[PLAY], &usec, NULL) < 0) {
1481 warn_pa("pa_stream_get_latency() failed");
1482 return -EIO;
1485 *(int *)rarg = bpus * usec;
1486 return 0;
1489 static ossp_action_fn_t action_fn_tbl[OSSP_NR_OPCODES] = {
1490 [OSSP_MIXER] = padsp_mixer,
1491 [OSSP_DSP_OPEN] = padsp_open,
1492 [OSSP_DSP_READ] = padsp_read,
1493 [OSSP_DSP_WRITE] = padsp_write,
1494 [OSSP_DSP_POLL] = padsp_poll,
1495 [OSSP_DSP_MMAP] = padsp_mmap,
1496 [OSSP_DSP_MUNMAP] = padsp_munmap,
1497 [OSSP_DSP_RESET] = padsp_flush,
1498 [OSSP_DSP_SYNC] = padsp_flush,
1499 [OSSP_DSP_POST] = padsp_post,
1500 [OSSP_DSP_GET_RATE] = padsp_get_param,
1501 [OSSP_DSP_GET_CHANNELS] = padsp_get_param,
1502 [OSSP_DSP_GET_FORMAT] = padsp_get_param,
1503 [OSSP_DSP_GET_BLKSIZE] = padsp_get_param,
1504 [OSSP_DSP_GET_FORMATS] = padsp_get_param,
1505 [OSSP_DSP_SET_RATE] = padsp_set_param,
1506 [OSSP_DSP_SET_CHANNELS] = padsp_set_param,
1507 [OSSP_DSP_SET_FORMAT] = padsp_set_param,
1508 [OSSP_DSP_SET_SUBDIVISION] = padsp_set_param,
1509 [OSSP_DSP_SET_FRAGMENT] = padsp_set_param,
1510 [OSSP_DSP_GET_TRIGGER] = padsp_get_param,
1511 [OSSP_DSP_SET_TRIGGER] = padsp_set_trigger,
1512 [OSSP_DSP_GET_OSPACE] = padsp_get_space,
1513 [OSSP_DSP_GET_ISPACE] = padsp_get_space,
1514 [OSSP_DSP_GET_OPTR] = padsp_get_ptr,
1515 [OSSP_DSP_GET_IPTR] = padsp_get_ptr,
1516 [OSSP_DSP_GET_ODELAY] = padsp_get_odelay,
1519 static int action_pre(void)
1521 pa_threaded_mainloop_lock(mainloop);
1522 if (fail_code) {
1523 pa_threaded_mainloop_unlock(mainloop);
1524 return fail_code;
1526 return 0;
1529 static void action_post(void)
1531 pa_threaded_mainloop_unlock(mainloop);
1534 int main(int argc, char **argv)
1536 int rc;
1538 ossp_slave_init(argc, argv);
1540 page_size = sysconf(_SC_PAGE_SIZE);
1542 mainloop = pa_threaded_mainloop_new();
1543 if (!mainloop) {
1544 err("failed to allocate mainloop");
1545 return 1;
1547 mainloop_api = pa_threaded_mainloop_get_api(mainloop);
1549 if (pa_threaded_mainloop_start(mainloop)) {
1550 err("pa_mainloop_start() failed");
1551 return 1;
1554 /* Okay, now we're open for business */
1555 rc = 0;
1556 do {
1557 rc = ossp_slave_process_command(ossp_cmd_fd, action_fn_tbl,
1558 action_pre, action_post);
1559 } while (rc > 0 && !fail_code);
1560 if (rc)
1561 fail_code = rc;
1563 pa_threaded_mainloop_lock(mainloop);
1565 kill_streams();
1566 if (context) {
1567 pa_context_disconnect(context);
1568 pa_context_unref(context);
1571 pa_threaded_mainloop_unlock(mainloop);
1573 pa_threaded_mainloop_stop(mainloop);
1574 pa_threaded_mainloop_free(mainloop);
1576 return fail_code ? 1 : 0;