153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2006 Lennart Poettering
553a5a1b3Sopenharmony_ci  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
653a5a1b3Sopenharmony_ci  Copyright 2009 Finn Thain
753a5a1b3Sopenharmony_ci
853a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
953a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
1053a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
1153a5a1b3Sopenharmony_ci  or (at your option) any later version.
1253a5a1b3Sopenharmony_ci
1353a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1453a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1553a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1653a5a1b3Sopenharmony_ci  General Public License for more details.
1753a5a1b3Sopenharmony_ci
1853a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1953a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
2053a5a1b3Sopenharmony_ci***/
2153a5a1b3Sopenharmony_ci
2253a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2353a5a1b3Sopenharmony_ci#include <config.h>
2453a5a1b3Sopenharmony_ci#endif
2553a5a1b3Sopenharmony_ci
2653a5a1b3Sopenharmony_ci#include <stdlib.h>
2753a5a1b3Sopenharmony_ci#include <stdio.h>
2853a5a1b3Sopenharmony_ci#include <errno.h>
2953a5a1b3Sopenharmony_ci#include <fcntl.h>
3053a5a1b3Sopenharmony_ci#include <unistd.h>
3153a5a1b3Sopenharmony_ci#include <sys/ioctl.h>
3253a5a1b3Sopenharmony_ci#include <sys/types.h>
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci#ifdef HAVE_POLL_H
3553a5a1b3Sopenharmony_ci#include <poll.h>
3653a5a1b3Sopenharmony_ci#endif
3753a5a1b3Sopenharmony_ci
3853a5a1b3Sopenharmony_ci#include <signal.h>
3953a5a1b3Sopenharmony_ci#include <stropts.h>
4053a5a1b3Sopenharmony_ci#include <sys/audio.h>
4153a5a1b3Sopenharmony_ci
4253a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_CONF_H
4353a5a1b3Sopenharmony_ci#include <sys/conf.h>
4453a5a1b3Sopenharmony_ci#endif
4553a5a1b3Sopenharmony_ci
4653a5a1b3Sopenharmony_ci#include <pulse/mainloop-signal.h>
4753a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
4853a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
4953a5a1b3Sopenharmony_ci#include <pulse/util.h>
5053a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
5153a5a1b3Sopenharmony_ci
5253a5a1b3Sopenharmony_ci#include <pulsecore/sink.h>
5353a5a1b3Sopenharmony_ci#include <pulsecore/source.h>
5453a5a1b3Sopenharmony_ci#include <pulsecore/module.h>
5553a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h>
5653a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
5753a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h>
5853a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
5953a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
6053a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h>
6153a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h>
6253a5a1b3Sopenharmony_ci#include <pulsecore/thread.h>
6353a5a1b3Sopenharmony_ci
6453a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
6553a5a1b3Sopenharmony_ci#include <pulsecore/time-smoother_2.h>
6653a5a1b3Sopenharmony_ci#else
6753a5a1b3Sopenharmony_ci#include <pulsecore/time-smoother.h>
6853a5a1b3Sopenharmony_ci#endif
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Pierre Ossman");
7153a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Solaris Sink/Source");
7253a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
7353a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
7453a5a1b3Sopenharmony_ci    "sink_name=<name for the sink> "
7553a5a1b3Sopenharmony_ci    "sink_properties=<properties for the sink> "
7653a5a1b3Sopenharmony_ci    "source_name=<name for the source> "
7753a5a1b3Sopenharmony_ci    "source_properties=<properties for the source> "
7853a5a1b3Sopenharmony_ci    "device=<audio device file name> "
7953a5a1b3Sopenharmony_ci    "record=<enable source?> "
8053a5a1b3Sopenharmony_ci    "playback=<enable sink?> "
8153a5a1b3Sopenharmony_ci    "format=<sample format> "
8253a5a1b3Sopenharmony_ci    "channels=<number of channels> "
8353a5a1b3Sopenharmony_ci    "rate=<sample rate> "
8453a5a1b3Sopenharmony_ci    "buffer_length=<milliseconds> "
8553a5a1b3Sopenharmony_ci    "channel_map=<channel map>");
8653a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false);
8753a5a1b3Sopenharmony_ci
8853a5a1b3Sopenharmony_cistruct userdata {
8953a5a1b3Sopenharmony_ci    pa_core *core;
9053a5a1b3Sopenharmony_ci    pa_sink *sink;
9153a5a1b3Sopenharmony_ci    pa_source *source;
9253a5a1b3Sopenharmony_ci
9353a5a1b3Sopenharmony_ci    pa_thread *thread;
9453a5a1b3Sopenharmony_ci    pa_thread_mq thread_mq;
9553a5a1b3Sopenharmony_ci    pa_rtpoll *rtpoll;
9653a5a1b3Sopenharmony_ci
9753a5a1b3Sopenharmony_ci    pa_signal_event *sig;
9853a5a1b3Sopenharmony_ci
9953a5a1b3Sopenharmony_ci    pa_memchunk memchunk;
10053a5a1b3Sopenharmony_ci
10153a5a1b3Sopenharmony_ci    uint32_t frame_size;
10253a5a1b3Sopenharmony_ci    int32_t buffer_size;
10353a5a1b3Sopenharmony_ci    uint64_t written_bytes, read_bytes;
10453a5a1b3Sopenharmony_ci
10553a5a1b3Sopenharmony_ci    char *device_name;
10653a5a1b3Sopenharmony_ci    int mode;
10753a5a1b3Sopenharmony_ci    int fd;
10853a5a1b3Sopenharmony_ci    pa_rtpoll_item *rtpoll_item;
10953a5a1b3Sopenharmony_ci    pa_module *module;
11053a5a1b3Sopenharmony_ci
11153a5a1b3Sopenharmony_ci    bool sink_suspended, source_suspended;
11253a5a1b3Sopenharmony_ci
11353a5a1b3Sopenharmony_ci    uint32_t play_samples_msw, record_samples_msw;
11453a5a1b3Sopenharmony_ci    uint32_t prev_playback_samples, prev_record_samples;
11553a5a1b3Sopenharmony_ci
11653a5a1b3Sopenharmony_ci    int32_t minimum_request;
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
11953a5a1b3Sopenharmony_ci    pa_smoother_2 *smoother;
12053a5a1b3Sopenharmony_ci#else
12153a5a1b3Sopenharmony_ci    pa_smoother *smoother;
12253a5a1b3Sopenharmony_ci#endif
12353a5a1b3Sopenharmony_ci};
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = {
12653a5a1b3Sopenharmony_ci    "sink_name",
12753a5a1b3Sopenharmony_ci    "sink_properties",
12853a5a1b3Sopenharmony_ci    "source_name",
12953a5a1b3Sopenharmony_ci    "source_properties",
13053a5a1b3Sopenharmony_ci    "device",
13153a5a1b3Sopenharmony_ci    "record",
13253a5a1b3Sopenharmony_ci    "playback",
13353a5a1b3Sopenharmony_ci    "buffer_length",
13453a5a1b3Sopenharmony_ci    "format",
13553a5a1b3Sopenharmony_ci    "rate",
13653a5a1b3Sopenharmony_ci    "channels",
13753a5a1b3Sopenharmony_ci    "channel_map",
13853a5a1b3Sopenharmony_ci    NULL
13953a5a1b3Sopenharmony_ci};
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_ci#define DEFAULT_DEVICE "/dev/audio"
14253a5a1b3Sopenharmony_ci
14353a5a1b3Sopenharmony_ci#define MAX_RENDER_HZ   (300)
14453a5a1b3Sopenharmony_ci/* This render rate limit imposes a minimum latency, but without it we waste too much CPU time. */
14553a5a1b3Sopenharmony_ci
14653a5a1b3Sopenharmony_ci#define MAX_BUFFER_SIZE (128 * 1024)
14753a5a1b3Sopenharmony_ci/* An attempt to buffer more than 128 KB causes write() to fail with errno == EAGAIN. */
14853a5a1b3Sopenharmony_ci
14953a5a1b3Sopenharmony_cistatic uint64_t get_playback_buffered_bytes(struct userdata *u) {
15053a5a1b3Sopenharmony_ci    audio_info_t info;
15153a5a1b3Sopenharmony_ci    uint64_t played_bytes;
15253a5a1b3Sopenharmony_ci    int err;
15353a5a1b3Sopenharmony_ci
15453a5a1b3Sopenharmony_ci    pa_assert(u->sink);
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_ci    err = ioctl(u->fd, AUDIO_GETINFO, &info);
15753a5a1b3Sopenharmony_ci    pa_assert(err >= 0);
15853a5a1b3Sopenharmony_ci
15953a5a1b3Sopenharmony_ci    /* Handle wrap-around of the device's sample counter, which is a uint_32. */
16053a5a1b3Sopenharmony_ci    if (u->prev_playback_samples > info.play.samples) {
16153a5a1b3Sopenharmony_ci        /*
16253a5a1b3Sopenharmony_ci         * Unfortunately info.play.samples can sometimes go backwards, even before it wraps!
16353a5a1b3Sopenharmony_ci         * The bug seems to be absent on Solaris x86 nv117 with audio810 driver, at least on this (UP) machine.
16453a5a1b3Sopenharmony_ci         * The bug is present on a different (SMP) machine running Solaris x86 nv103 with audioens driver.
16553a5a1b3Sopenharmony_ci         * An earlier revision of this file mentions the same bug independently (unknown configuration).
16653a5a1b3Sopenharmony_ci         */
16753a5a1b3Sopenharmony_ci        if (u->prev_playback_samples + info.play.samples < 240000) {
16853a5a1b3Sopenharmony_ci            ++u->play_samples_msw;
16953a5a1b3Sopenharmony_ci        } else {
17053a5a1b3Sopenharmony_ci            pa_log_debug("play.samples went backwards %d bytes", u->prev_playback_samples - info.play.samples);
17153a5a1b3Sopenharmony_ci        }
17253a5a1b3Sopenharmony_ci    }
17353a5a1b3Sopenharmony_ci    u->prev_playback_samples = info.play.samples;
17453a5a1b3Sopenharmony_ci    played_bytes = (((uint64_t)u->play_samples_msw << 32) + info.play.samples) * u->frame_size;
17553a5a1b3Sopenharmony_ci
17653a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
17753a5a1b3Sopenharmony_ci    pa_smoother_2_put(u->smoother, pa_rtclock_now(), played_bytes);
17853a5a1b3Sopenharmony_ci#else
17953a5a1b3Sopenharmony_ci    pa_smoother_put(u->smoother, pa_rtclock_now(), pa_bytes_to_usec(played_bytes, &u->sink->sample_spec));
18053a5a1b3Sopenharmony_ci#endif
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_ci    if (u->written_bytes > played_bytes)
18353a5a1b3Sopenharmony_ci        return u->written_bytes - played_bytes;
18453a5a1b3Sopenharmony_ci    else
18553a5a1b3Sopenharmony_ci        return 0;
18653a5a1b3Sopenharmony_ci}
18753a5a1b3Sopenharmony_ci
18853a5a1b3Sopenharmony_cistatic pa_usec_t sink_get_latency(struct userdata *u, pa_sample_spec *ss) {
18953a5a1b3Sopenharmony_ci    pa_usec_t r = 0;
19053a5a1b3Sopenharmony_ci
19153a5a1b3Sopenharmony_ci    pa_assert(u);
19253a5a1b3Sopenharmony_ci    pa_assert(ss);
19353a5a1b3Sopenharmony_ci
19453a5a1b3Sopenharmony_ci    if (u->fd >= 0) {
19553a5a1b3Sopenharmony_ci        r = pa_bytes_to_usec(get_playback_buffered_bytes(u), ss);
19653a5a1b3Sopenharmony_ci        if (u->memchunk.memblock)
19753a5a1b3Sopenharmony_ci            r += pa_bytes_to_usec(u->memchunk.length, ss);
19853a5a1b3Sopenharmony_ci    }
19953a5a1b3Sopenharmony_ci    return r;
20053a5a1b3Sopenharmony_ci}
20153a5a1b3Sopenharmony_ci
20253a5a1b3Sopenharmony_cistatic uint64_t get_recorded_bytes(struct userdata *u) {
20353a5a1b3Sopenharmony_ci    audio_info_t info;
20453a5a1b3Sopenharmony_ci    uint64_t result;
20553a5a1b3Sopenharmony_ci    int err;
20653a5a1b3Sopenharmony_ci
20753a5a1b3Sopenharmony_ci    pa_assert(u->source);
20853a5a1b3Sopenharmony_ci
20953a5a1b3Sopenharmony_ci    err = ioctl(u->fd, AUDIO_GETINFO, &info);
21053a5a1b3Sopenharmony_ci    pa_assert(err >= 0);
21153a5a1b3Sopenharmony_ci
21253a5a1b3Sopenharmony_ci    if (u->prev_record_samples > info.record.samples)
21353a5a1b3Sopenharmony_ci        ++u->record_samples_msw;
21453a5a1b3Sopenharmony_ci    u->prev_record_samples = info.record.samples;
21553a5a1b3Sopenharmony_ci    result = (((uint64_t)u->record_samples_msw << 32) + info.record.samples) * u->frame_size;
21653a5a1b3Sopenharmony_ci
21753a5a1b3Sopenharmony_ci    return result;
21853a5a1b3Sopenharmony_ci}
21953a5a1b3Sopenharmony_ci
22053a5a1b3Sopenharmony_cistatic pa_usec_t source_get_latency(struct userdata *u, pa_sample_spec *ss) {
22153a5a1b3Sopenharmony_ci    pa_usec_t r = 0;
22253a5a1b3Sopenharmony_ci    audio_info_t info;
22353a5a1b3Sopenharmony_ci
22453a5a1b3Sopenharmony_ci    pa_assert(u);
22553a5a1b3Sopenharmony_ci    pa_assert(ss);
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci    if (u->fd) {
22853a5a1b3Sopenharmony_ci        int err = ioctl(u->fd, AUDIO_GETINFO, &info);
22953a5a1b3Sopenharmony_ci        pa_assert(err >= 0);
23053a5a1b3Sopenharmony_ci
23153a5a1b3Sopenharmony_ci        r = pa_bytes_to_usec(get_recorded_bytes(u), ss) - pa_bytes_to_usec(u->read_bytes, ss);
23253a5a1b3Sopenharmony_ci    }
23353a5a1b3Sopenharmony_ci    return r;
23453a5a1b3Sopenharmony_ci}
23553a5a1b3Sopenharmony_ci
23653a5a1b3Sopenharmony_cistatic void build_pollfd(struct userdata *u) {
23753a5a1b3Sopenharmony_ci    struct pollfd *pollfd;
23853a5a1b3Sopenharmony_ci
23953a5a1b3Sopenharmony_ci    pa_assert(u);
24053a5a1b3Sopenharmony_ci    pa_assert(!u->rtpoll_item);
24153a5a1b3Sopenharmony_ci    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
24253a5a1b3Sopenharmony_ci
24353a5a1b3Sopenharmony_ci    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
24453a5a1b3Sopenharmony_ci    pollfd->fd = u->fd;
24553a5a1b3Sopenharmony_ci    pollfd->events = 0;
24653a5a1b3Sopenharmony_ci    pollfd->revents = 0;
24753a5a1b3Sopenharmony_ci}
24853a5a1b3Sopenharmony_ci
24953a5a1b3Sopenharmony_cistatic int set_buffer(int fd, int buffer_size) {
25053a5a1b3Sopenharmony_ci    audio_info_t info;
25153a5a1b3Sopenharmony_ci
25253a5a1b3Sopenharmony_ci    pa_assert(fd >= 0);
25353a5a1b3Sopenharmony_ci
25453a5a1b3Sopenharmony_ci    AUDIO_INITINFO(&info);
25553a5a1b3Sopenharmony_ci    info.play.buffer_size = buffer_size;
25653a5a1b3Sopenharmony_ci    info.record.buffer_size = buffer_size;
25753a5a1b3Sopenharmony_ci
25853a5a1b3Sopenharmony_ci    if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
25953a5a1b3Sopenharmony_ci        if (errno == EINVAL)
26053a5a1b3Sopenharmony_ci            pa_log("AUDIO_SETINFO: Unsupported buffer size.");
26153a5a1b3Sopenharmony_ci        else
26253a5a1b3Sopenharmony_ci            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
26353a5a1b3Sopenharmony_ci        return -1;
26453a5a1b3Sopenharmony_ci    }
26553a5a1b3Sopenharmony_ci
26653a5a1b3Sopenharmony_ci    return 0;
26753a5a1b3Sopenharmony_ci}
26853a5a1b3Sopenharmony_ci
26953a5a1b3Sopenharmony_cistatic int auto_format(int fd, int mode, pa_sample_spec *ss) {
27053a5a1b3Sopenharmony_ci    audio_info_t info;
27153a5a1b3Sopenharmony_ci
27253a5a1b3Sopenharmony_ci    pa_assert(fd >= 0);
27353a5a1b3Sopenharmony_ci    pa_assert(ss);
27453a5a1b3Sopenharmony_ci
27553a5a1b3Sopenharmony_ci    AUDIO_INITINFO(&info);
27653a5a1b3Sopenharmony_ci
27753a5a1b3Sopenharmony_ci    if (mode != O_RDONLY) {
27853a5a1b3Sopenharmony_ci        info.play.sample_rate = ss->rate;
27953a5a1b3Sopenharmony_ci        info.play.channels = ss->channels;
28053a5a1b3Sopenharmony_ci        switch (ss->format) {
28153a5a1b3Sopenharmony_ci        case PA_SAMPLE_U8:
28253a5a1b3Sopenharmony_ci            info.play.precision = 8;
28353a5a1b3Sopenharmony_ci            info.play.encoding = AUDIO_ENCODING_LINEAR;
28453a5a1b3Sopenharmony_ci            break;
28553a5a1b3Sopenharmony_ci        case PA_SAMPLE_ALAW:
28653a5a1b3Sopenharmony_ci            info.play.precision = 8;
28753a5a1b3Sopenharmony_ci            info.play.encoding = AUDIO_ENCODING_ALAW;
28853a5a1b3Sopenharmony_ci            break;
28953a5a1b3Sopenharmony_ci        case PA_SAMPLE_ULAW:
29053a5a1b3Sopenharmony_ci            info.play.precision = 8;
29153a5a1b3Sopenharmony_ci            info.play.encoding = AUDIO_ENCODING_ULAW;
29253a5a1b3Sopenharmony_ci            break;
29353a5a1b3Sopenharmony_ci        case PA_SAMPLE_S16NE:
29453a5a1b3Sopenharmony_ci            info.play.precision = 16;
29553a5a1b3Sopenharmony_ci            info.play.encoding = AUDIO_ENCODING_LINEAR;
29653a5a1b3Sopenharmony_ci            break;
29753a5a1b3Sopenharmony_ci        default:
29853a5a1b3Sopenharmony_ci            pa_log("AUDIO_SETINFO: Unsupported sample format.");
29953a5a1b3Sopenharmony_ci            return -1;
30053a5a1b3Sopenharmony_ci        }
30153a5a1b3Sopenharmony_ci    }
30253a5a1b3Sopenharmony_ci
30353a5a1b3Sopenharmony_ci    if (mode != O_WRONLY) {
30453a5a1b3Sopenharmony_ci        info.record.sample_rate = ss->rate;
30553a5a1b3Sopenharmony_ci        info.record.channels = ss->channels;
30653a5a1b3Sopenharmony_ci        switch (ss->format) {
30753a5a1b3Sopenharmony_ci        case PA_SAMPLE_U8:
30853a5a1b3Sopenharmony_ci            info.record.precision = 8;
30953a5a1b3Sopenharmony_ci            info.record.encoding = AUDIO_ENCODING_LINEAR;
31053a5a1b3Sopenharmony_ci            break;
31153a5a1b3Sopenharmony_ci        case PA_SAMPLE_ALAW:
31253a5a1b3Sopenharmony_ci            info.record.precision = 8;
31353a5a1b3Sopenharmony_ci            info.record.encoding = AUDIO_ENCODING_ALAW;
31453a5a1b3Sopenharmony_ci            break;
31553a5a1b3Sopenharmony_ci        case PA_SAMPLE_ULAW:
31653a5a1b3Sopenharmony_ci            info.record.precision = 8;
31753a5a1b3Sopenharmony_ci            info.record.encoding = AUDIO_ENCODING_ULAW;
31853a5a1b3Sopenharmony_ci            break;
31953a5a1b3Sopenharmony_ci        case PA_SAMPLE_S16NE:
32053a5a1b3Sopenharmony_ci            info.record.precision = 16;
32153a5a1b3Sopenharmony_ci            info.record.encoding = AUDIO_ENCODING_LINEAR;
32253a5a1b3Sopenharmony_ci            break;
32353a5a1b3Sopenharmony_ci        default:
32453a5a1b3Sopenharmony_ci            pa_log("AUDIO_SETINFO: Unsupported sample format.");
32553a5a1b3Sopenharmony_ci            return -1;
32653a5a1b3Sopenharmony_ci        }
32753a5a1b3Sopenharmony_ci    }
32853a5a1b3Sopenharmony_ci
32953a5a1b3Sopenharmony_ci    if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
33053a5a1b3Sopenharmony_ci        if (errno == EINVAL)
33153a5a1b3Sopenharmony_ci            pa_log("AUDIO_SETINFO: Failed to set sample format.");
33253a5a1b3Sopenharmony_ci        else
33353a5a1b3Sopenharmony_ci            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
33453a5a1b3Sopenharmony_ci        return -1;
33553a5a1b3Sopenharmony_ci    }
33653a5a1b3Sopenharmony_ci
33753a5a1b3Sopenharmony_ci    return 0;
33853a5a1b3Sopenharmony_ci}
33953a5a1b3Sopenharmony_ci
34053a5a1b3Sopenharmony_cistatic int open_audio_device(struct userdata *u, pa_sample_spec *ss) {
34153a5a1b3Sopenharmony_ci    pa_assert(u);
34253a5a1b3Sopenharmony_ci    pa_assert(ss);
34353a5a1b3Sopenharmony_ci
34453a5a1b3Sopenharmony_ci    if ((u->fd = pa_open_cloexec(u->device_name, u->mode | O_NONBLOCK, 0)) < 0) {
34553a5a1b3Sopenharmony_ci        pa_log_warn("open %s failed (%s)", u->device_name, pa_cstrerror(errno));
34653a5a1b3Sopenharmony_ci        return -1;
34753a5a1b3Sopenharmony_ci    }
34853a5a1b3Sopenharmony_ci
34953a5a1b3Sopenharmony_ci    pa_log_info("device opened in %s mode.", u->mode == O_WRONLY ? "O_WRONLY" : (u->mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
35053a5a1b3Sopenharmony_ci
35153a5a1b3Sopenharmony_ci    if (auto_format(u->fd, u->mode, ss) < 0)
35253a5a1b3Sopenharmony_ci        return -1;
35353a5a1b3Sopenharmony_ci
35453a5a1b3Sopenharmony_ci    if (set_buffer(u->fd, u->buffer_size) < 0)
35553a5a1b3Sopenharmony_ci        return -1;
35653a5a1b3Sopenharmony_ci
35753a5a1b3Sopenharmony_ci    u->written_bytes = u->read_bytes = 0;
35853a5a1b3Sopenharmony_ci    u->play_samples_msw = u->record_samples_msw = 0;
35953a5a1b3Sopenharmony_ci    u->prev_playback_samples = u->prev_record_samples = 0;
36053a5a1b3Sopenharmony_ci
36153a5a1b3Sopenharmony_ci    return u->fd;
36253a5a1b3Sopenharmony_ci}
36353a5a1b3Sopenharmony_ci
36453a5a1b3Sopenharmony_cistatic void suspend(struct userdata *u) {
36553a5a1b3Sopenharmony_ci    pa_assert(u);
36653a5a1b3Sopenharmony_ci    pa_assert(u->fd >= 0);
36753a5a1b3Sopenharmony_ci
36853a5a1b3Sopenharmony_ci    pa_log_info("Suspending...");
36953a5a1b3Sopenharmony_ci
37053a5a1b3Sopenharmony_ci    ioctl(u->fd, I_FLUSH, FLUSHRW);
37153a5a1b3Sopenharmony_ci    pa_close(u->fd);
37253a5a1b3Sopenharmony_ci    u->fd = -1;
37353a5a1b3Sopenharmony_ci
37453a5a1b3Sopenharmony_ci    if (u->rtpoll_item) {
37553a5a1b3Sopenharmony_ci        pa_rtpoll_item_free(u->rtpoll_item);
37653a5a1b3Sopenharmony_ci        u->rtpoll_item = NULL;
37753a5a1b3Sopenharmony_ci    }
37853a5a1b3Sopenharmony_ci
37953a5a1b3Sopenharmony_ci    pa_log_info("Device suspended.");
38053a5a1b3Sopenharmony_ci}
38153a5a1b3Sopenharmony_ci
38253a5a1b3Sopenharmony_cistatic int unsuspend(struct userdata *u) {
38353a5a1b3Sopenharmony_ci    pa_assert(u);
38453a5a1b3Sopenharmony_ci    pa_assert(u->fd < 0);
38553a5a1b3Sopenharmony_ci
38653a5a1b3Sopenharmony_ci    pa_log_info("Resuming...");
38753a5a1b3Sopenharmony_ci
38853a5a1b3Sopenharmony_ci    if (open_audio_device(u, u->sink ? &u->sink->sample_spec : &u->source->sample_spec) < 0)
38953a5a1b3Sopenharmony_ci        return -1;
39053a5a1b3Sopenharmony_ci
39153a5a1b3Sopenharmony_ci    build_pollfd(u);
39253a5a1b3Sopenharmony_ci
39353a5a1b3Sopenharmony_ci    pa_log_info("Device resumed.");
39453a5a1b3Sopenharmony_ci
39553a5a1b3Sopenharmony_ci    return 0;
39653a5a1b3Sopenharmony_ci}
39753a5a1b3Sopenharmony_ci
39853a5a1b3Sopenharmony_cistatic int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
39953a5a1b3Sopenharmony_ci    struct userdata *u = PA_SINK(o)->userdata;
40053a5a1b3Sopenharmony_ci
40153a5a1b3Sopenharmony_ci    switch (code) {
40253a5a1b3Sopenharmony_ci
40353a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_LATENCY:
40453a5a1b3Sopenharmony_ci            *((int64_t*) data) = sink_get_latency(u, &PA_SINK(o)->sample_spec);
40553a5a1b3Sopenharmony_ci            return 0;
40653a5a1b3Sopenharmony_ci    }
40753a5a1b3Sopenharmony_ci
40853a5a1b3Sopenharmony_ci    return pa_sink_process_msg(o, code, data, offset, chunk);
40953a5a1b3Sopenharmony_ci}
41053a5a1b3Sopenharmony_ci
41153a5a1b3Sopenharmony_ci/* Called from the IO thread. */
41253a5a1b3Sopenharmony_cistatic int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
41353a5a1b3Sopenharmony_ci    struct userdata *u;
41453a5a1b3Sopenharmony_ci
41553a5a1b3Sopenharmony_ci    pa_assert(s);
41653a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
41753a5a1b3Sopenharmony_ci
41853a5a1b3Sopenharmony_ci    /* It may be that only the suspend cause is changing, in which case there's
41953a5a1b3Sopenharmony_ci     * nothing to do. */
42053a5a1b3Sopenharmony_ci    if (new_state == s->thread_info.state)
42153a5a1b3Sopenharmony_ci        return 0;
42253a5a1b3Sopenharmony_ci
42353a5a1b3Sopenharmony_ci    switch (new_state) {
42453a5a1b3Sopenharmony_ci
42553a5a1b3Sopenharmony_ci        case PA_SINK_SUSPENDED:
42653a5a1b3Sopenharmony_ci
42753a5a1b3Sopenharmony_ci            pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
42853a5a1b3Sopenharmony_ci
42953a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
43053a5a1b3Sopenharmony_ci            pa_smoother_2_pause(u->smoother, pa_rtclock_now());
43153a5a1b3Sopenharmony_ci#else
43253a5a1b3Sopenharmony_ci            pa_smoother_pause(u->smoother, pa_rtclock_now());
43353a5a1b3Sopenharmony_ci#endif
43453a5a1b3Sopenharmony_ci
43553a5a1b3Sopenharmony_ci            if (!u->source || u->source_suspended)
43653a5a1b3Sopenharmony_ci                suspend(u);
43753a5a1b3Sopenharmony_ci
43853a5a1b3Sopenharmony_ci            u->sink_suspended = true;
43953a5a1b3Sopenharmony_ci            break;
44053a5a1b3Sopenharmony_ci
44153a5a1b3Sopenharmony_ci        case PA_SINK_IDLE:
44253a5a1b3Sopenharmony_ci        case PA_SINK_RUNNING:
44353a5a1b3Sopenharmony_ci
44453a5a1b3Sopenharmony_ci            if (s->thread_info.state == PA_SINK_SUSPENDED) {
44553a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
44653a5a1b3Sopenharmony_ci                pa_smoother_2_resume(u->smoother, pa_rtclock_now());
44753a5a1b3Sopenharmony_ci#else
44853a5a1b3Sopenharmony_ci                pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
44953a5a1b3Sopenharmony_ci#endif
45053a5a1b3Sopenharmony_ci
45153a5a1b3Sopenharmony_ci                if (!u->source || u->source_suspended) {
45253a5a1b3Sopenharmony_ci                    bool mute;
45353a5a1b3Sopenharmony_ci                    if (unsuspend(u) < 0)
45453a5a1b3Sopenharmony_ci                        return -1;
45553a5a1b3Sopenharmony_ci                    s->get_volume(s);
45653a5a1b3Sopenharmony_ci                    if (s->get_mute(s, &mute) >= 0)
45753a5a1b3Sopenharmony_ci                        pa_sink_set_mute(s, mute, false);
45853a5a1b3Sopenharmony_ci                }
45953a5a1b3Sopenharmony_ci                u->sink_suspended = false;
46053a5a1b3Sopenharmony_ci            }
46153a5a1b3Sopenharmony_ci            break;
46253a5a1b3Sopenharmony_ci
46353a5a1b3Sopenharmony_ci        case PA_SINK_INVALID_STATE:
46453a5a1b3Sopenharmony_ci        case PA_SINK_UNLINKED:
46553a5a1b3Sopenharmony_ci        case PA_SINK_INIT:
46653a5a1b3Sopenharmony_ci            ;
46753a5a1b3Sopenharmony_ci    }
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_ci    return 0;
47053a5a1b3Sopenharmony_ci}
47153a5a1b3Sopenharmony_ci
47253a5a1b3Sopenharmony_cistatic int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
47353a5a1b3Sopenharmony_ci    struct userdata *u = PA_SOURCE(o)->userdata;
47453a5a1b3Sopenharmony_ci
47553a5a1b3Sopenharmony_ci    switch (code) {
47653a5a1b3Sopenharmony_ci
47753a5a1b3Sopenharmony_ci        case PA_SOURCE_MESSAGE_GET_LATENCY:
47853a5a1b3Sopenharmony_ci            *((pa_usec_t*) data) = source_get_latency(u, &PA_SOURCE(o)->sample_spec);
47953a5a1b3Sopenharmony_ci            return 0;
48053a5a1b3Sopenharmony_ci    }
48153a5a1b3Sopenharmony_ci
48253a5a1b3Sopenharmony_ci    return pa_source_process_msg(o, code, data, offset, chunk);
48353a5a1b3Sopenharmony_ci}
48453a5a1b3Sopenharmony_ci
48553a5a1b3Sopenharmony_ci/* Called from the IO thread. */
48653a5a1b3Sopenharmony_cistatic int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
48753a5a1b3Sopenharmony_ci    struct userdata *u;
48853a5a1b3Sopenharmony_ci
48953a5a1b3Sopenharmony_ci    pa_assert(s);
49053a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
49153a5a1b3Sopenharmony_ci
49253a5a1b3Sopenharmony_ci    /* It may be that only the suspend cause is changing, in which case there's
49353a5a1b3Sopenharmony_ci     * nothing to do. */
49453a5a1b3Sopenharmony_ci    if (new_state == s->thread_info.state)
49553a5a1b3Sopenharmony_ci        return 0;
49653a5a1b3Sopenharmony_ci
49753a5a1b3Sopenharmony_ci    switch (new_state) {
49853a5a1b3Sopenharmony_ci
49953a5a1b3Sopenharmony_ci        case PA_SOURCE_SUSPENDED:
50053a5a1b3Sopenharmony_ci
50153a5a1b3Sopenharmony_ci            pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
50253a5a1b3Sopenharmony_ci
50353a5a1b3Sopenharmony_ci            if (!u->sink || u->sink_suspended)
50453a5a1b3Sopenharmony_ci                suspend(u);
50553a5a1b3Sopenharmony_ci
50653a5a1b3Sopenharmony_ci            u->source_suspended = true;
50753a5a1b3Sopenharmony_ci            break;
50853a5a1b3Sopenharmony_ci
50953a5a1b3Sopenharmony_ci        case PA_SOURCE_IDLE:
51053a5a1b3Sopenharmony_ci        case PA_SOURCE_RUNNING:
51153a5a1b3Sopenharmony_ci
51253a5a1b3Sopenharmony_ci            if (s->thread_info.state == PA_SOURCE_SUSPENDED) {
51353a5a1b3Sopenharmony_ci                if (!u->sink || u->sink_suspended) {
51453a5a1b3Sopenharmony_ci                    if (unsuspend(u) < 0)
51553a5a1b3Sopenharmony_ci                        return -1;
51653a5a1b3Sopenharmony_ci                    s->get_volume(s);
51753a5a1b3Sopenharmony_ci                }
51853a5a1b3Sopenharmony_ci                u->source_suspended = false;
51953a5a1b3Sopenharmony_ci            }
52053a5a1b3Sopenharmony_ci            break;
52153a5a1b3Sopenharmony_ci
52253a5a1b3Sopenharmony_ci        case PA_SOURCE_UNLINKED:
52353a5a1b3Sopenharmony_ci        case PA_SOURCE_INIT:
52453a5a1b3Sopenharmony_ci        case PA_SOURCE_INVALID_STATE:
52553a5a1b3Sopenharmony_ci            ;
52653a5a1b3Sopenharmony_ci
52753a5a1b3Sopenharmony_ci    }
52853a5a1b3Sopenharmony_ci
52953a5a1b3Sopenharmony_ci    return 0;
53053a5a1b3Sopenharmony_ci}
53153a5a1b3Sopenharmony_ci
53253a5a1b3Sopenharmony_cistatic void sink_set_volume(pa_sink *s) {
53353a5a1b3Sopenharmony_ci    struct userdata *u;
53453a5a1b3Sopenharmony_ci    audio_info_t info;
53553a5a1b3Sopenharmony_ci
53653a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
53753a5a1b3Sopenharmony_ci
53853a5a1b3Sopenharmony_ci    if (u->fd >= 0) {
53953a5a1b3Sopenharmony_ci        AUDIO_INITINFO(&info);
54053a5a1b3Sopenharmony_ci
54153a5a1b3Sopenharmony_ci        info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
54253a5a1b3Sopenharmony_ci        pa_assert(info.play.gain <= AUDIO_MAX_GAIN);
54353a5a1b3Sopenharmony_ci
54453a5a1b3Sopenharmony_ci        if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
54553a5a1b3Sopenharmony_ci            if (errno == EINVAL)
54653a5a1b3Sopenharmony_ci                pa_log("AUDIO_SETINFO: Unsupported volume.");
54753a5a1b3Sopenharmony_ci            else
54853a5a1b3Sopenharmony_ci                pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
54953a5a1b3Sopenharmony_ci        }
55053a5a1b3Sopenharmony_ci    }
55153a5a1b3Sopenharmony_ci}
55253a5a1b3Sopenharmony_ci
55353a5a1b3Sopenharmony_cistatic void sink_get_volume(pa_sink *s) {
55453a5a1b3Sopenharmony_ci    struct userdata *u;
55553a5a1b3Sopenharmony_ci    audio_info_t info;
55653a5a1b3Sopenharmony_ci
55753a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
55853a5a1b3Sopenharmony_ci
55953a5a1b3Sopenharmony_ci    if (u->fd >= 0) {
56053a5a1b3Sopenharmony_ci        if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
56153a5a1b3Sopenharmony_ci            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
56253a5a1b3Sopenharmony_ci        else
56353a5a1b3Sopenharmony_ci            pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
56453a5a1b3Sopenharmony_ci    }
56553a5a1b3Sopenharmony_ci}
56653a5a1b3Sopenharmony_ci
56753a5a1b3Sopenharmony_cistatic void source_set_volume(pa_source *s) {
56853a5a1b3Sopenharmony_ci    struct userdata *u;
56953a5a1b3Sopenharmony_ci    audio_info_t info;
57053a5a1b3Sopenharmony_ci
57153a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
57253a5a1b3Sopenharmony_ci
57353a5a1b3Sopenharmony_ci    if (u->fd >= 0) {
57453a5a1b3Sopenharmony_ci        AUDIO_INITINFO(&info);
57553a5a1b3Sopenharmony_ci
57653a5a1b3Sopenharmony_ci        info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
57753a5a1b3Sopenharmony_ci        pa_assert(info.play.gain <= AUDIO_MAX_GAIN);
57853a5a1b3Sopenharmony_ci
57953a5a1b3Sopenharmony_ci        if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
58053a5a1b3Sopenharmony_ci            if (errno == EINVAL)
58153a5a1b3Sopenharmony_ci                pa_log("AUDIO_SETINFO: Unsupported volume.");
58253a5a1b3Sopenharmony_ci            else
58353a5a1b3Sopenharmony_ci                pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
58453a5a1b3Sopenharmony_ci        }
58553a5a1b3Sopenharmony_ci    }
58653a5a1b3Sopenharmony_ci}
58753a5a1b3Sopenharmony_ci
58853a5a1b3Sopenharmony_cistatic void source_get_volume(pa_source *s) {
58953a5a1b3Sopenharmony_ci    struct userdata *u;
59053a5a1b3Sopenharmony_ci    audio_info_t info;
59153a5a1b3Sopenharmony_ci
59253a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
59353a5a1b3Sopenharmony_ci
59453a5a1b3Sopenharmony_ci    if (u->fd >= 0) {
59553a5a1b3Sopenharmony_ci        if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
59653a5a1b3Sopenharmony_ci            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
59753a5a1b3Sopenharmony_ci        else
59853a5a1b3Sopenharmony_ci            pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
59953a5a1b3Sopenharmony_ci    }
60053a5a1b3Sopenharmony_ci}
60153a5a1b3Sopenharmony_ci
60253a5a1b3Sopenharmony_cistatic void sink_set_mute(pa_sink *s) {
60353a5a1b3Sopenharmony_ci    struct userdata *u = s->userdata;
60453a5a1b3Sopenharmony_ci    audio_info_t info;
60553a5a1b3Sopenharmony_ci
60653a5a1b3Sopenharmony_ci    pa_assert(u);
60753a5a1b3Sopenharmony_ci
60853a5a1b3Sopenharmony_ci    if (u->fd >= 0) {
60953a5a1b3Sopenharmony_ci        AUDIO_INITINFO(&info);
61053a5a1b3Sopenharmony_ci
61153a5a1b3Sopenharmony_ci        info.output_muted = s->muted;
61253a5a1b3Sopenharmony_ci
61353a5a1b3Sopenharmony_ci        if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
61453a5a1b3Sopenharmony_ci            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
61553a5a1b3Sopenharmony_ci    }
61653a5a1b3Sopenharmony_ci}
61753a5a1b3Sopenharmony_ci
61853a5a1b3Sopenharmony_cistatic int sink_get_mute(pa_sink *s, bool *mute) {
61953a5a1b3Sopenharmony_ci    struct userdata *u = s->userdata;
62053a5a1b3Sopenharmony_ci    audio_info_t info;
62153a5a1b3Sopenharmony_ci
62253a5a1b3Sopenharmony_ci    pa_assert(u);
62353a5a1b3Sopenharmony_ci
62453a5a1b3Sopenharmony_ci    if (u->fd < 0)
62553a5a1b3Sopenharmony_ci        return -1;
62653a5a1b3Sopenharmony_ci
62753a5a1b3Sopenharmony_ci    if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0) {
62853a5a1b3Sopenharmony_ci        pa_log("AUDIO_GETINFO: %s", pa_cstrerror(errno));
62953a5a1b3Sopenharmony_ci        return -1;
63053a5a1b3Sopenharmony_ci    }
63153a5a1b3Sopenharmony_ci
63253a5a1b3Sopenharmony_ci    *mute = info.output_muted;
63353a5a1b3Sopenharmony_ci
63453a5a1b3Sopenharmony_ci    return 0;
63553a5a1b3Sopenharmony_ci}
63653a5a1b3Sopenharmony_ci
63753a5a1b3Sopenharmony_cistatic void process_rewind(struct userdata *u) {
63853a5a1b3Sopenharmony_ci    size_t rewind_nbytes;
63953a5a1b3Sopenharmony_ci
64053a5a1b3Sopenharmony_ci    pa_assert(u);
64153a5a1b3Sopenharmony_ci
64253a5a1b3Sopenharmony_ci    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
64353a5a1b3Sopenharmony_ci        pa_sink_process_rewind(u->sink, 0);
64453a5a1b3Sopenharmony_ci        return;
64553a5a1b3Sopenharmony_ci    }
64653a5a1b3Sopenharmony_ci
64753a5a1b3Sopenharmony_ci    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
64853a5a1b3Sopenharmony_ci
64953a5a1b3Sopenharmony_ci    if (rewind_nbytes > 0) {
65053a5a1b3Sopenharmony_ci        pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
65153a5a1b3Sopenharmony_ci        rewind_nbytes = PA_MIN(u->memchunk.length, rewind_nbytes);
65253a5a1b3Sopenharmony_ci        u->memchunk.length -= rewind_nbytes;
65353a5a1b3Sopenharmony_ci        if (u->memchunk.length <= 0 && u->memchunk.memblock) {
65453a5a1b3Sopenharmony_ci            pa_memblock_unref(u->memchunk.memblock);
65553a5a1b3Sopenharmony_ci            pa_memchunk_reset(&u->memchunk);
65653a5a1b3Sopenharmony_ci        }
65753a5a1b3Sopenharmony_ci        pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
65853a5a1b3Sopenharmony_ci    }
65953a5a1b3Sopenharmony_ci
66053a5a1b3Sopenharmony_ci    pa_sink_process_rewind(u->sink, rewind_nbytes);
66153a5a1b3Sopenharmony_ci}
66253a5a1b3Sopenharmony_ci
66353a5a1b3Sopenharmony_cistatic void thread_func(void *userdata) {
66453a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
66553a5a1b3Sopenharmony_ci    unsigned short revents = 0;
66653a5a1b3Sopenharmony_ci    int ret, err;
66753a5a1b3Sopenharmony_ci    audio_info_t info;
66853a5a1b3Sopenharmony_ci
66953a5a1b3Sopenharmony_ci    pa_assert(u);
67053a5a1b3Sopenharmony_ci
67153a5a1b3Sopenharmony_ci    pa_log_debug("Thread starting up");
67253a5a1b3Sopenharmony_ci
67353a5a1b3Sopenharmony_ci    if (u->core->realtime_scheduling)
67453a5a1b3Sopenharmony_ci        pa_thread_make_realtime(u->core->realtime_priority);
67553a5a1b3Sopenharmony_ci
67653a5a1b3Sopenharmony_ci    pa_thread_mq_install(&u->thread_mq);
67753a5a1b3Sopenharmony_ci
67853a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
67953a5a1b3Sopenharmony_ci    pa_smoother_2_reset(u->smoother, pa_rtclock_now());
68053a5a1b3Sopenharmony_ci#else
68153a5a1b3Sopenharmony_ci    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
68253a5a1b3Sopenharmony_ci#endif
68353a5a1b3Sopenharmony_ci
68453a5a1b3Sopenharmony_ci    for (;;) {
68553a5a1b3Sopenharmony_ci        /* Render some data and write it to the dsp */
68653a5a1b3Sopenharmony_ci
68753a5a1b3Sopenharmony_ci        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
68853a5a1b3Sopenharmony_ci            process_rewind(u);
68953a5a1b3Sopenharmony_ci
69053a5a1b3Sopenharmony_ci        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
69153a5a1b3Sopenharmony_ci            pa_usec_t xtime0, ysleep_interval, xsleep_interval;
69253a5a1b3Sopenharmony_ci            uint64_t buffered_bytes;
69353a5a1b3Sopenharmony_ci
69453a5a1b3Sopenharmony_ci            err = ioctl(u->fd, AUDIO_GETINFO, &info);
69553a5a1b3Sopenharmony_ci            if (err < 0) {
69653a5a1b3Sopenharmony_ci                pa_log("AUDIO_GETINFO ioctl failed: %s", pa_cstrerror(errno));
69753a5a1b3Sopenharmony_ci                goto fail;
69853a5a1b3Sopenharmony_ci            }
69953a5a1b3Sopenharmony_ci
70053a5a1b3Sopenharmony_ci            if (info.play.error) {
70153a5a1b3Sopenharmony_ci                pa_log_debug("buffer under-run!");
70253a5a1b3Sopenharmony_ci
70353a5a1b3Sopenharmony_ci                AUDIO_INITINFO(&info);
70453a5a1b3Sopenharmony_ci                info.play.error = 0;
70553a5a1b3Sopenharmony_ci                if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
70653a5a1b3Sopenharmony_ci                    pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
70753a5a1b3Sopenharmony_ci
70853a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
70953a5a1b3Sopenharmony_ci                pa_smoother_2_reset(u->smoother, pa_rtclock_now());
71053a5a1b3Sopenharmony_ci#else
71153a5a1b3Sopenharmony_ci                pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
71253a5a1b3Sopenharmony_ci#endif
71353a5a1b3Sopenharmony_ci            }
71453a5a1b3Sopenharmony_ci
71553a5a1b3Sopenharmony_ci            for (;;) {
71653a5a1b3Sopenharmony_ci                void *p;
71753a5a1b3Sopenharmony_ci                ssize_t w;
71853a5a1b3Sopenharmony_ci                size_t len;
71953a5a1b3Sopenharmony_ci                int write_type = 1;
72053a5a1b3Sopenharmony_ci
72153a5a1b3Sopenharmony_ci                /*
72253a5a1b3Sopenharmony_ci                 * Since we cannot modify the size of the output buffer we fake it
72353a5a1b3Sopenharmony_ci                 * by not filling it more than u->buffer_size.
72453a5a1b3Sopenharmony_ci                 */
72553a5a1b3Sopenharmony_ci                xtime0 = pa_rtclock_now();
72653a5a1b3Sopenharmony_ci                buffered_bytes = get_playback_buffered_bytes(u);
72753a5a1b3Sopenharmony_ci                if (buffered_bytes >= (uint64_t)u->buffer_size)
72853a5a1b3Sopenharmony_ci                    break;
72953a5a1b3Sopenharmony_ci
73053a5a1b3Sopenharmony_ci                len = u->buffer_size - buffered_bytes;
73153a5a1b3Sopenharmony_ci                len -= len % u->frame_size;
73253a5a1b3Sopenharmony_ci
73353a5a1b3Sopenharmony_ci                if (len < (size_t) u->minimum_request)
73453a5a1b3Sopenharmony_ci                    break;
73553a5a1b3Sopenharmony_ci
73653a5a1b3Sopenharmony_ci                if (!u->memchunk.length)
73753a5a1b3Sopenharmony_ci                    pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk);
73853a5a1b3Sopenharmony_ci
73953a5a1b3Sopenharmony_ci                len = PA_MIN(u->memchunk.length, len);
74053a5a1b3Sopenharmony_ci
74153a5a1b3Sopenharmony_ci                p = pa_memblock_acquire(u->memchunk.memblock);
74253a5a1b3Sopenharmony_ci                w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, len, &write_type);
74353a5a1b3Sopenharmony_ci                pa_memblock_release(u->memchunk.memblock);
74453a5a1b3Sopenharmony_ci
74553a5a1b3Sopenharmony_ci                if (w <= 0) {
74653a5a1b3Sopenharmony_ci                    if (errno == EAGAIN) {
74753a5a1b3Sopenharmony_ci                        /* We may have realtime priority so yield the CPU to ensure that fd can become writable again. */
74853a5a1b3Sopenharmony_ci                        pa_log_debug("EAGAIN with %llu bytes buffered.", buffered_bytes);
74953a5a1b3Sopenharmony_ci                        break;
75053a5a1b3Sopenharmony_ci                    } else {
75153a5a1b3Sopenharmony_ci                        pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
75253a5a1b3Sopenharmony_ci                        goto fail;
75353a5a1b3Sopenharmony_ci                    }
75453a5a1b3Sopenharmony_ci                } else {
75553a5a1b3Sopenharmony_ci                    pa_assert(w % u->frame_size == 0);
75653a5a1b3Sopenharmony_ci
75753a5a1b3Sopenharmony_ci                    u->written_bytes += w;
75853a5a1b3Sopenharmony_ci                    u->memchunk.index += w;
75953a5a1b3Sopenharmony_ci                    u->memchunk.length -= w;
76053a5a1b3Sopenharmony_ci                    if (u->memchunk.length <= 0) {
76153a5a1b3Sopenharmony_ci                        pa_memblock_unref(u->memchunk.memblock);
76253a5a1b3Sopenharmony_ci                        pa_memchunk_reset(&u->memchunk);
76353a5a1b3Sopenharmony_ci                    }
76453a5a1b3Sopenharmony_ci                }
76553a5a1b3Sopenharmony_ci            }
76653a5a1b3Sopenharmony_ci
76753a5a1b3Sopenharmony_ci            ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec);
76853a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
76953a5a1b3Sopenharmony_ci            xsleep_interval = pa_smoother_2_translate(u->smoother, ysleep_interval);
77053a5a1b3Sopenharmony_ci#else
77153a5a1b3Sopenharmony_ci            xsleep_interval = pa_smoother_translate(u->smoother, xtime0, ysleep_interval);
77253a5a1b3Sopenharmony_ci#endif
77353a5a1b3Sopenharmony_ci            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + PA_MIN(xsleep_interval, ysleep_interval));
77453a5a1b3Sopenharmony_ci        } else
77553a5a1b3Sopenharmony_ci            pa_rtpoll_set_timer_disabled(u->rtpoll);
77653a5a1b3Sopenharmony_ci
77753a5a1b3Sopenharmony_ci        /* Try to read some data and pass it on to the source driver */
77853a5a1b3Sopenharmony_ci
77953a5a1b3Sopenharmony_ci        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && (revents & POLLIN)) {
78053a5a1b3Sopenharmony_ci            pa_memchunk memchunk;
78153a5a1b3Sopenharmony_ci            void *p;
78253a5a1b3Sopenharmony_ci            ssize_t r;
78353a5a1b3Sopenharmony_ci            size_t len;
78453a5a1b3Sopenharmony_ci
78553a5a1b3Sopenharmony_ci            err = ioctl(u->fd, AUDIO_GETINFO, &info);
78653a5a1b3Sopenharmony_ci            pa_assert(err >= 0);
78753a5a1b3Sopenharmony_ci
78853a5a1b3Sopenharmony_ci            if (info.record.error) {
78953a5a1b3Sopenharmony_ci                pa_log_debug("buffer overflow!");
79053a5a1b3Sopenharmony_ci
79153a5a1b3Sopenharmony_ci                AUDIO_INITINFO(&info);
79253a5a1b3Sopenharmony_ci                info.record.error = 0;
79353a5a1b3Sopenharmony_ci                if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
79453a5a1b3Sopenharmony_ci                    pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
79553a5a1b3Sopenharmony_ci            }
79653a5a1b3Sopenharmony_ci
79753a5a1b3Sopenharmony_ci            err = ioctl(u->fd, I_NREAD, &len);
79853a5a1b3Sopenharmony_ci            pa_assert(err >= 0);
79953a5a1b3Sopenharmony_ci
80053a5a1b3Sopenharmony_ci            if (len > 0) {
80153a5a1b3Sopenharmony_ci                memchunk.memblock = pa_memblock_new(u->core->mempool, len);
80253a5a1b3Sopenharmony_ci                pa_assert(memchunk.memblock);
80353a5a1b3Sopenharmony_ci
80453a5a1b3Sopenharmony_ci                p = pa_memblock_acquire(memchunk.memblock);
80553a5a1b3Sopenharmony_ci                r = pa_read(u->fd, p, len, NULL);
80653a5a1b3Sopenharmony_ci                pa_memblock_release(memchunk.memblock);
80753a5a1b3Sopenharmony_ci
80853a5a1b3Sopenharmony_ci                if (r < 0) {
80953a5a1b3Sopenharmony_ci                    pa_memblock_unref(memchunk.memblock);
81053a5a1b3Sopenharmony_ci                    if (errno == EAGAIN)
81153a5a1b3Sopenharmony_ci                        break;
81253a5a1b3Sopenharmony_ci                    else {
81353a5a1b3Sopenharmony_ci                        pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
81453a5a1b3Sopenharmony_ci                        goto fail;
81553a5a1b3Sopenharmony_ci                    }
81653a5a1b3Sopenharmony_ci                } else {
81753a5a1b3Sopenharmony_ci                    u->read_bytes += r;
81853a5a1b3Sopenharmony_ci
81953a5a1b3Sopenharmony_ci                    memchunk.index = 0;
82053a5a1b3Sopenharmony_ci                    memchunk.length = r;
82153a5a1b3Sopenharmony_ci
82253a5a1b3Sopenharmony_ci                    pa_source_post(u->source, &memchunk);
82353a5a1b3Sopenharmony_ci                    pa_memblock_unref(memchunk.memblock);
82453a5a1b3Sopenharmony_ci
82553a5a1b3Sopenharmony_ci                    revents &= ~POLLIN;
82653a5a1b3Sopenharmony_ci                }
82753a5a1b3Sopenharmony_ci            }
82853a5a1b3Sopenharmony_ci        }
82953a5a1b3Sopenharmony_ci
83053a5a1b3Sopenharmony_ci        if (u->rtpoll_item) {
83153a5a1b3Sopenharmony_ci            struct pollfd *pollfd;
83253a5a1b3Sopenharmony_ci
83353a5a1b3Sopenharmony_ci            pa_assert(u->fd >= 0);
83453a5a1b3Sopenharmony_ci
83553a5a1b3Sopenharmony_ci            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
83653a5a1b3Sopenharmony_ci            pollfd->events = (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0;
83753a5a1b3Sopenharmony_ci        }
83853a5a1b3Sopenharmony_ci
83953a5a1b3Sopenharmony_ci        /* Hmm, nothing to do. Let's sleep */
84053a5a1b3Sopenharmony_ci        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
84153a5a1b3Sopenharmony_ci            goto fail;
84253a5a1b3Sopenharmony_ci
84353a5a1b3Sopenharmony_ci        if (ret == 0)
84453a5a1b3Sopenharmony_ci            goto finish;
84553a5a1b3Sopenharmony_ci
84653a5a1b3Sopenharmony_ci        if (u->rtpoll_item) {
84753a5a1b3Sopenharmony_ci            struct pollfd *pollfd;
84853a5a1b3Sopenharmony_ci
84953a5a1b3Sopenharmony_ci            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
85053a5a1b3Sopenharmony_ci
85153a5a1b3Sopenharmony_ci            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
85253a5a1b3Sopenharmony_ci                pa_log("DSP shutdown.");
85353a5a1b3Sopenharmony_ci                goto fail;
85453a5a1b3Sopenharmony_ci            }
85553a5a1b3Sopenharmony_ci
85653a5a1b3Sopenharmony_ci            revents = pollfd->revents;
85753a5a1b3Sopenharmony_ci        } else
85853a5a1b3Sopenharmony_ci            revents = 0;
85953a5a1b3Sopenharmony_ci    }
86053a5a1b3Sopenharmony_ci
86153a5a1b3Sopenharmony_cifail:
86253a5a1b3Sopenharmony_ci    /* We have to continue processing messages until we receive the
86353a5a1b3Sopenharmony_ci     * SHUTDOWN message */
86453a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
86553a5a1b3Sopenharmony_ci    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
86653a5a1b3Sopenharmony_ci
86753a5a1b3Sopenharmony_cifinish:
86853a5a1b3Sopenharmony_ci    pa_log_debug("Thread shutting down");
86953a5a1b3Sopenharmony_ci}
87053a5a1b3Sopenharmony_ci
87153a5a1b3Sopenharmony_cistatic void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
87253a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
87353a5a1b3Sopenharmony_ci
87453a5a1b3Sopenharmony_ci    pa_assert(u);
87553a5a1b3Sopenharmony_ci
87653a5a1b3Sopenharmony_ci    pa_log_debug("caught signal");
87753a5a1b3Sopenharmony_ci
87853a5a1b3Sopenharmony_ci    if (u->sink) {
87953a5a1b3Sopenharmony_ci        pa_sink_get_volume(u->sink, true);
88053a5a1b3Sopenharmony_ci        pa_sink_get_mute(u->sink, true);
88153a5a1b3Sopenharmony_ci    }
88253a5a1b3Sopenharmony_ci
88353a5a1b3Sopenharmony_ci    if (u->source)
88453a5a1b3Sopenharmony_ci        pa_source_get_volume(u->source, true);
88553a5a1b3Sopenharmony_ci}
88653a5a1b3Sopenharmony_ci
88753a5a1b3Sopenharmony_ciint pa__init(pa_module *m) {
88853a5a1b3Sopenharmony_ci    struct userdata *u = NULL;
88953a5a1b3Sopenharmony_ci    bool record = true, playback = true;
89053a5a1b3Sopenharmony_ci    pa_sample_spec ss;
89153a5a1b3Sopenharmony_ci    pa_channel_map map;
89253a5a1b3Sopenharmony_ci    pa_modargs *ma = NULL;
89353a5a1b3Sopenharmony_ci    uint32_t buffer_length_msec;
89453a5a1b3Sopenharmony_ci    int fd = -1;
89553a5a1b3Sopenharmony_ci    pa_sink_new_data sink_new_data;
89653a5a1b3Sopenharmony_ci    pa_source_new_data source_new_data;
89753a5a1b3Sopenharmony_ci    char const *name;
89853a5a1b3Sopenharmony_ci    char *name_buf;
89953a5a1b3Sopenharmony_ci    bool namereg_fail;
90053a5a1b3Sopenharmony_ci
90153a5a1b3Sopenharmony_ci    pa_assert(m);
90253a5a1b3Sopenharmony_ci
90353a5a1b3Sopenharmony_ci    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
90453a5a1b3Sopenharmony_ci        pa_log("failed to parse module arguments.");
90553a5a1b3Sopenharmony_ci        goto fail;
90653a5a1b3Sopenharmony_ci    }
90753a5a1b3Sopenharmony_ci
90853a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
90953a5a1b3Sopenharmony_ci        pa_log("record= and playback= expect a boolean argument.");
91053a5a1b3Sopenharmony_ci        goto fail;
91153a5a1b3Sopenharmony_ci    }
91253a5a1b3Sopenharmony_ci
91353a5a1b3Sopenharmony_ci    if (!playback && !record) {
91453a5a1b3Sopenharmony_ci        pa_log("neither playback nor record enabled for device.");
91553a5a1b3Sopenharmony_ci        goto fail;
91653a5a1b3Sopenharmony_ci    }
91753a5a1b3Sopenharmony_ci
91853a5a1b3Sopenharmony_ci    u = pa_xnew0(struct userdata, 1);
91953a5a1b3Sopenharmony_ci
92053a5a1b3Sopenharmony_ci#ifndef USE_SMOOTHER_2
92153a5a1b3Sopenharmony_ci    if (!(u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC * 2, true, true, 10, pa_rtclock_now(), true)))
92253a5a1b3Sopenharmony_ci        goto fail;
92353a5a1b3Sopenharmony_ci#endif
92453a5a1b3Sopenharmony_ci
92553a5a1b3Sopenharmony_ci    /*
92653a5a1b3Sopenharmony_ci     * For a process (or several processes) to use the same audio device for both
92753a5a1b3Sopenharmony_ci     * record and playback at the same time, the device's mixer must be enabled.
92853a5a1b3Sopenharmony_ci     * See mixerctl(1). It may be turned off for playback only or record only.
92953a5a1b3Sopenharmony_ci     */
93053a5a1b3Sopenharmony_ci    u->mode = (playback && record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
93153a5a1b3Sopenharmony_ci
93253a5a1b3Sopenharmony_ci    ss = m->core->default_sample_spec;
93353a5a1b3Sopenharmony_ci    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
93453a5a1b3Sopenharmony_ci        pa_log("failed to parse sample specification");
93553a5a1b3Sopenharmony_ci        goto fail;
93653a5a1b3Sopenharmony_ci    }
93753a5a1b3Sopenharmony_ci    u->frame_size = pa_frame_size(&ss);
93853a5a1b3Sopenharmony_ci
93953a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
94053a5a1b3Sopenharmony_ci    u->smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, pa_rtclock_now(), u->frame_size, ss.rate);
94153a5a1b3Sopenharmony_ci#endif
94253a5a1b3Sopenharmony_ci
94353a5a1b3Sopenharmony_ci    u->minimum_request = pa_usec_to_bytes(PA_USEC_PER_SEC / MAX_RENDER_HZ, &ss);
94453a5a1b3Sopenharmony_ci
94553a5a1b3Sopenharmony_ci    buffer_length_msec = 100;
94653a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_u32(ma, "buffer_length", &buffer_length_msec) < 0) {
94753a5a1b3Sopenharmony_ci        pa_log("failed to parse buffer_length argument");
94853a5a1b3Sopenharmony_ci        goto fail;
94953a5a1b3Sopenharmony_ci    }
95053a5a1b3Sopenharmony_ci    u->buffer_size = pa_usec_to_bytes(1000 * buffer_length_msec, &ss);
95153a5a1b3Sopenharmony_ci    if (u->buffer_size < 2 * u->minimum_request) {
95253a5a1b3Sopenharmony_ci        pa_log("buffer_length argument cannot be smaller than %u",
95353a5a1b3Sopenharmony_ci               (unsigned)(pa_bytes_to_usec(2 * u->minimum_request, &ss) / 1000));
95453a5a1b3Sopenharmony_ci        goto fail;
95553a5a1b3Sopenharmony_ci    }
95653a5a1b3Sopenharmony_ci    if (u->buffer_size > MAX_BUFFER_SIZE) {
95753a5a1b3Sopenharmony_ci        pa_log("buffer_length argument cannot be greater than %u",
95853a5a1b3Sopenharmony_ci               (unsigned)(pa_bytes_to_usec(MAX_BUFFER_SIZE, &ss) / 1000));
95953a5a1b3Sopenharmony_ci        goto fail;
96053a5a1b3Sopenharmony_ci    }
96153a5a1b3Sopenharmony_ci
96253a5a1b3Sopenharmony_ci    u->device_name = pa_xstrdup(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
96353a5a1b3Sopenharmony_ci
96453a5a1b3Sopenharmony_ci    if ((fd = open_audio_device(u, &ss)) < 0)
96553a5a1b3Sopenharmony_ci        goto fail;
96653a5a1b3Sopenharmony_ci
96753a5a1b3Sopenharmony_ci    u->core = m->core;
96853a5a1b3Sopenharmony_ci    u->module = m;
96953a5a1b3Sopenharmony_ci    m->userdata = u;
97053a5a1b3Sopenharmony_ci
97153a5a1b3Sopenharmony_ci    pa_memchunk_reset(&u->memchunk);
97253a5a1b3Sopenharmony_ci
97353a5a1b3Sopenharmony_ci    u->rtpoll = pa_rtpoll_new();
97453a5a1b3Sopenharmony_ci
97553a5a1b3Sopenharmony_ci    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
97653a5a1b3Sopenharmony_ci        pa_log("pa_thread_mq_init() failed.");
97753a5a1b3Sopenharmony_ci        goto fail;
97853a5a1b3Sopenharmony_ci    }
97953a5a1b3Sopenharmony_ci
98053a5a1b3Sopenharmony_ci    u->rtpoll_item = NULL;
98153a5a1b3Sopenharmony_ci    build_pollfd(u);
98253a5a1b3Sopenharmony_ci
98353a5a1b3Sopenharmony_ci    if (u->mode != O_WRONLY) {
98453a5a1b3Sopenharmony_ci        name_buf = NULL;
98553a5a1b3Sopenharmony_ci        namereg_fail = true;
98653a5a1b3Sopenharmony_ci
98753a5a1b3Sopenharmony_ci        if (!(name = pa_modargs_get_value(ma, "source_name", NULL))) {
98853a5a1b3Sopenharmony_ci            name = name_buf = pa_sprintf_malloc("solaris_input.%s", pa_path_get_filename(u->device_name));
98953a5a1b3Sopenharmony_ci            namereg_fail = false;
99053a5a1b3Sopenharmony_ci        }
99153a5a1b3Sopenharmony_ci
99253a5a1b3Sopenharmony_ci        pa_source_new_data_init(&source_new_data);
99353a5a1b3Sopenharmony_ci        source_new_data.driver = __FILE__;
99453a5a1b3Sopenharmony_ci        source_new_data.module = m;
99553a5a1b3Sopenharmony_ci        pa_source_new_data_set_name(&source_new_data, name);
99653a5a1b3Sopenharmony_ci        source_new_data.namereg_fail = namereg_fail;
99753a5a1b3Sopenharmony_ci        pa_source_new_data_set_sample_spec(&source_new_data, &ss);
99853a5a1b3Sopenharmony_ci        pa_source_new_data_set_channel_map(&source_new_data, &map);
99953a5a1b3Sopenharmony_ci        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
100053a5a1b3Sopenharmony_ci        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "solaris");
100153a5a1b3Sopenharmony_ci        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Solaris PCM source");
100253a5a1b3Sopenharmony_ci        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "serial");
100353a5a1b3Sopenharmony_ci        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) u->buffer_size);
100453a5a1b3Sopenharmony_ci
100553a5a1b3Sopenharmony_ci        if (pa_modargs_get_proplist(ma, "source_properties", source_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
100653a5a1b3Sopenharmony_ci            pa_log("Invalid properties");
100753a5a1b3Sopenharmony_ci            pa_source_new_data_done(&source_new_data);
100853a5a1b3Sopenharmony_ci            goto fail;
100953a5a1b3Sopenharmony_ci        }
101053a5a1b3Sopenharmony_ci
101153a5a1b3Sopenharmony_ci        u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
101253a5a1b3Sopenharmony_ci        pa_source_new_data_done(&source_new_data);
101353a5a1b3Sopenharmony_ci        pa_xfree(name_buf);
101453a5a1b3Sopenharmony_ci
101553a5a1b3Sopenharmony_ci        if (!u->source) {
101653a5a1b3Sopenharmony_ci            pa_log("Failed to create source object");
101753a5a1b3Sopenharmony_ci            goto fail;
101853a5a1b3Sopenharmony_ci        }
101953a5a1b3Sopenharmony_ci
102053a5a1b3Sopenharmony_ci        u->source->userdata = u;
102153a5a1b3Sopenharmony_ci        u->source->parent.process_msg = source_process_msg;
102253a5a1b3Sopenharmony_ci        u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
102353a5a1b3Sopenharmony_ci
102453a5a1b3Sopenharmony_ci        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
102553a5a1b3Sopenharmony_ci        pa_source_set_rtpoll(u->source, u->rtpoll);
102653a5a1b3Sopenharmony_ci        pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->source->sample_spec));
102753a5a1b3Sopenharmony_ci
102853a5a1b3Sopenharmony_ci        pa_source_set_get_volume_callback(u->source, source_get_volume);
102953a5a1b3Sopenharmony_ci        pa_source_set_set_volume_callback(u->source, source_set_volume);
103053a5a1b3Sopenharmony_ci        u->source->refresh_volume = true;
103153a5a1b3Sopenharmony_ci    } else
103253a5a1b3Sopenharmony_ci        u->source = NULL;
103353a5a1b3Sopenharmony_ci
103453a5a1b3Sopenharmony_ci    if (u->mode != O_RDONLY) {
103553a5a1b3Sopenharmony_ci        name_buf = NULL;
103653a5a1b3Sopenharmony_ci        namereg_fail = true;
103753a5a1b3Sopenharmony_ci        if (!(name = pa_modargs_get_value(ma, "sink_name", NULL))) {
103853a5a1b3Sopenharmony_ci            name = name_buf = pa_sprintf_malloc("solaris_output.%s", pa_path_get_filename(u->device_name));
103953a5a1b3Sopenharmony_ci            namereg_fail = false;
104053a5a1b3Sopenharmony_ci        }
104153a5a1b3Sopenharmony_ci
104253a5a1b3Sopenharmony_ci        pa_sink_new_data_init(&sink_new_data);
104353a5a1b3Sopenharmony_ci        sink_new_data.driver = __FILE__;
104453a5a1b3Sopenharmony_ci        sink_new_data.module = m;
104553a5a1b3Sopenharmony_ci        pa_sink_new_data_set_name(&sink_new_data, name);
104653a5a1b3Sopenharmony_ci        sink_new_data.namereg_fail = namereg_fail;
104753a5a1b3Sopenharmony_ci        pa_sink_new_data_set_sample_spec(&sink_new_data, &ss);
104853a5a1b3Sopenharmony_ci        pa_sink_new_data_set_channel_map(&sink_new_data, &map);
104953a5a1b3Sopenharmony_ci        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
105053a5a1b3Sopenharmony_ci        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "solaris");
105153a5a1b3Sopenharmony_ci        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Solaris PCM sink");
105253a5a1b3Sopenharmony_ci        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "serial");
105353a5a1b3Sopenharmony_ci
105453a5a1b3Sopenharmony_ci        if (pa_modargs_get_proplist(ma, "sink_properties", sink_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
105553a5a1b3Sopenharmony_ci            pa_log("Invalid properties");
105653a5a1b3Sopenharmony_ci            pa_sink_new_data_done(&sink_new_data);
105753a5a1b3Sopenharmony_ci            goto fail;
105853a5a1b3Sopenharmony_ci        }
105953a5a1b3Sopenharmony_ci
106053a5a1b3Sopenharmony_ci        u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
106153a5a1b3Sopenharmony_ci        pa_sink_new_data_done(&sink_new_data);
106253a5a1b3Sopenharmony_ci
106353a5a1b3Sopenharmony_ci        pa_assert(u->sink);
106453a5a1b3Sopenharmony_ci        u->sink->userdata = u;
106553a5a1b3Sopenharmony_ci        u->sink->parent.process_msg = sink_process_msg;
106653a5a1b3Sopenharmony_ci        u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
106753a5a1b3Sopenharmony_ci
106853a5a1b3Sopenharmony_ci        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
106953a5a1b3Sopenharmony_ci        pa_sink_set_rtpoll(u->sink, u->rtpoll);
107053a5a1b3Sopenharmony_ci        pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->buffer_size, &u->sink->sample_spec));
107153a5a1b3Sopenharmony_ci        pa_sink_set_max_request(u->sink, u->buffer_size);
107253a5a1b3Sopenharmony_ci        pa_sink_set_max_rewind(u->sink, u->buffer_size);
107353a5a1b3Sopenharmony_ci
107453a5a1b3Sopenharmony_ci        pa_sink_set_get_volume_callback(u->sink, sink_get_volume);
107553a5a1b3Sopenharmony_ci        pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
107653a5a1b3Sopenharmony_ci        pa_sink_set_get_mute_callback(u->sink, sink_get_mute);
107753a5a1b3Sopenharmony_ci        pa_sink_set_set_mute_callback(u->sink, sink_set_mute);
107853a5a1b3Sopenharmony_ci        u->sink->refresh_volume = u->sink->refresh_muted = true;
107953a5a1b3Sopenharmony_ci    } else
108053a5a1b3Sopenharmony_ci        u->sink = NULL;
108153a5a1b3Sopenharmony_ci
108253a5a1b3Sopenharmony_ci    pa_assert(u->source || u->sink);
108353a5a1b3Sopenharmony_ci
108453a5a1b3Sopenharmony_ci    u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
108553a5a1b3Sopenharmony_ci    if (u->sig)
108653a5a1b3Sopenharmony_ci        ioctl(u->fd, I_SETSIG, S_MSG);
108753a5a1b3Sopenharmony_ci    else
108853a5a1b3Sopenharmony_ci        pa_log_warn("Could not register SIGPOLL handler");
108953a5a1b3Sopenharmony_ci
109053a5a1b3Sopenharmony_ci    if (!(u->thread = pa_thread_new("solaris", thread_func, u))) {
109153a5a1b3Sopenharmony_ci        pa_log("Failed to create thread.");
109253a5a1b3Sopenharmony_ci        goto fail;
109353a5a1b3Sopenharmony_ci    }
109453a5a1b3Sopenharmony_ci
109553a5a1b3Sopenharmony_ci    /* Read mixer settings */
109653a5a1b3Sopenharmony_ci    if (u->sink) {
109753a5a1b3Sopenharmony_ci        if (sink_new_data.volume_is_set)
109853a5a1b3Sopenharmony_ci            u->sink->set_volume(u->sink);
109953a5a1b3Sopenharmony_ci        else
110053a5a1b3Sopenharmony_ci            u->sink->get_volume(u->sink);
110153a5a1b3Sopenharmony_ci
110253a5a1b3Sopenharmony_ci        if (sink_new_data.muted_is_set)
110353a5a1b3Sopenharmony_ci            u->sink->set_mute(u->sink);
110453a5a1b3Sopenharmony_ci        else {
110553a5a1b3Sopenharmony_ci            bool mute;
110653a5a1b3Sopenharmony_ci
110753a5a1b3Sopenharmony_ci            if (u->sink->get_mute(u->sink, &mute) >= 0)
110853a5a1b3Sopenharmony_ci                pa_sink_set_mute(u->sink, mute, false);
110953a5a1b3Sopenharmony_ci        }
111053a5a1b3Sopenharmony_ci
111153a5a1b3Sopenharmony_ci        pa_sink_put(u->sink);
111253a5a1b3Sopenharmony_ci    }
111353a5a1b3Sopenharmony_ci
111453a5a1b3Sopenharmony_ci    if (u->source) {
111553a5a1b3Sopenharmony_ci        if (source_new_data.volume_is_set)
111653a5a1b3Sopenharmony_ci            u->source->set_volume(u->source);
111753a5a1b3Sopenharmony_ci        else
111853a5a1b3Sopenharmony_ci            u->source->get_volume(u->source);
111953a5a1b3Sopenharmony_ci
112053a5a1b3Sopenharmony_ci        pa_source_put(u->source);
112153a5a1b3Sopenharmony_ci    }
112253a5a1b3Sopenharmony_ci
112353a5a1b3Sopenharmony_ci    pa_modargs_free(ma);
112453a5a1b3Sopenharmony_ci
112553a5a1b3Sopenharmony_ci    return 0;
112653a5a1b3Sopenharmony_ci
112753a5a1b3Sopenharmony_cifail:
112853a5a1b3Sopenharmony_ci    if (u)
112953a5a1b3Sopenharmony_ci        pa__done(m);
113053a5a1b3Sopenharmony_ci    else if (fd >= 0)
113153a5a1b3Sopenharmony_ci        close(fd);
113253a5a1b3Sopenharmony_ci
113353a5a1b3Sopenharmony_ci    if (ma)
113453a5a1b3Sopenharmony_ci        pa_modargs_free(ma);
113553a5a1b3Sopenharmony_ci
113653a5a1b3Sopenharmony_ci    return -1;
113753a5a1b3Sopenharmony_ci}
113853a5a1b3Sopenharmony_ci
113953a5a1b3Sopenharmony_civoid pa__done(pa_module *m) {
114053a5a1b3Sopenharmony_ci    struct userdata *u;
114153a5a1b3Sopenharmony_ci
114253a5a1b3Sopenharmony_ci    pa_assert(m);
114353a5a1b3Sopenharmony_ci
114453a5a1b3Sopenharmony_ci    if (!(u = m->userdata))
114553a5a1b3Sopenharmony_ci        return;
114653a5a1b3Sopenharmony_ci
114753a5a1b3Sopenharmony_ci    if (u->sig) {
114853a5a1b3Sopenharmony_ci        ioctl(u->fd, I_SETSIG, 0);
114953a5a1b3Sopenharmony_ci        pa_signal_free(u->sig);
115053a5a1b3Sopenharmony_ci    }
115153a5a1b3Sopenharmony_ci
115253a5a1b3Sopenharmony_ci    if (u->sink)
115353a5a1b3Sopenharmony_ci        pa_sink_unlink(u->sink);
115453a5a1b3Sopenharmony_ci
115553a5a1b3Sopenharmony_ci    if (u->source)
115653a5a1b3Sopenharmony_ci        pa_source_unlink(u->source);
115753a5a1b3Sopenharmony_ci
115853a5a1b3Sopenharmony_ci    if (u->thread) {
115953a5a1b3Sopenharmony_ci        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
116053a5a1b3Sopenharmony_ci        pa_thread_free(u->thread);
116153a5a1b3Sopenharmony_ci    }
116253a5a1b3Sopenharmony_ci
116353a5a1b3Sopenharmony_ci    pa_thread_mq_done(&u->thread_mq);
116453a5a1b3Sopenharmony_ci
116553a5a1b3Sopenharmony_ci    if (u->sink)
116653a5a1b3Sopenharmony_ci        pa_sink_unref(u->sink);
116753a5a1b3Sopenharmony_ci
116853a5a1b3Sopenharmony_ci    if (u->source)
116953a5a1b3Sopenharmony_ci        pa_source_unref(u->source);
117053a5a1b3Sopenharmony_ci
117153a5a1b3Sopenharmony_ci    if (u->memchunk.memblock)
117253a5a1b3Sopenharmony_ci        pa_memblock_unref(u->memchunk.memblock);
117353a5a1b3Sopenharmony_ci
117453a5a1b3Sopenharmony_ci    if (u->rtpoll_item)
117553a5a1b3Sopenharmony_ci        pa_rtpoll_item_free(u->rtpoll_item);
117653a5a1b3Sopenharmony_ci
117753a5a1b3Sopenharmony_ci    if (u->rtpoll)
117853a5a1b3Sopenharmony_ci        pa_rtpoll_free(u->rtpoll);
117953a5a1b3Sopenharmony_ci
118053a5a1b3Sopenharmony_ci    if (u->fd >= 0)
118153a5a1b3Sopenharmony_ci        close(u->fd);
118253a5a1b3Sopenharmony_ci
118353a5a1b3Sopenharmony_ci    if (u->smoother)
118453a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
118553a5a1b3Sopenharmony_ci        pa_smoother_2_free(u->smoother);
118653a5a1b3Sopenharmony_ci#else
118753a5a1b3Sopenharmony_ci        pa_smoother_free(u->smoother);
118853a5a1b3Sopenharmony_ci#endif
118953a5a1b3Sopenharmony_ci
119053a5a1b3Sopenharmony_ci    pa_xfree(u->device_name);
119153a5a1b3Sopenharmony_ci
119253a5a1b3Sopenharmony_ci    pa_xfree(u);
119353a5a1b3Sopenharmony_ci}
1194