153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2004-2006 Lennart Poettering
553a5a1b3Sopenharmony_ci  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
953a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
1053a5a1b3Sopenharmony_ci  or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci  General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1853a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1953a5a1b3Sopenharmony_ci***/
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci/* General power management rules:
2253a5a1b3Sopenharmony_ci *
2353a5a1b3Sopenharmony_ci *   When SUSPENDED we close the audio device.
2453a5a1b3Sopenharmony_ci *
2553a5a1b3Sopenharmony_ci *   We make no difference between IDLE and RUNNING in our handling.
2653a5a1b3Sopenharmony_ci *
2753a5a1b3Sopenharmony_ci *   As long as we are in RUNNING/IDLE state we will *always* write data to
2853a5a1b3Sopenharmony_ci *   the device. If none is available from the inputs, we write silence
2953a5a1b3Sopenharmony_ci *   instead.
3053a5a1b3Sopenharmony_ci *
3153a5a1b3Sopenharmony_ci *   If power should be saved on IDLE module-suspend-on-idle should be used.
3253a5a1b3Sopenharmony_ci *
3353a5a1b3Sopenharmony_ci */
3453a5a1b3Sopenharmony_ci
3553a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
3653a5a1b3Sopenharmony_ci#include <config.h>
3753a5a1b3Sopenharmony_ci#endif
3853a5a1b3Sopenharmony_ci
3953a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_MMAN_H
4053a5a1b3Sopenharmony_ci#include <sys/mman.h>
4153a5a1b3Sopenharmony_ci#endif
4253a5a1b3Sopenharmony_ci
4353a5a1b3Sopenharmony_ci#include <sys/soundcard.h>
4453a5a1b3Sopenharmony_ci#include <sys/ioctl.h>
4553a5a1b3Sopenharmony_ci#include <stdlib.h>
4653a5a1b3Sopenharmony_ci#include <stdio.h>
4753a5a1b3Sopenharmony_ci#include <errno.h>
4853a5a1b3Sopenharmony_ci#include <fcntl.h>
4953a5a1b3Sopenharmony_ci#include <unistd.h>
5053a5a1b3Sopenharmony_ci
5153a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
5253a5a1b3Sopenharmony_ci#include <pulse/util.h>
5353a5a1b3Sopenharmony_ci
5453a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
5553a5a1b3Sopenharmony_ci#include <pulsecore/thread.h>
5653a5a1b3Sopenharmony_ci#include <pulsecore/sink.h>
5753a5a1b3Sopenharmony_ci#include <pulsecore/source.h>
5853a5a1b3Sopenharmony_ci#include <pulsecore/module.h>
5953a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h>
6053a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
6153a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h>
6253a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
6353a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
6453a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h>
6553a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h>
6653a5a1b3Sopenharmony_ci#include <pulsecore/poll.h>
6753a5a1b3Sopenharmony_ci
6853a5a1b3Sopenharmony_ci#if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
6953a5a1b3Sopenharmony_ci#include <sys/audioio.h>
7053a5a1b3Sopenharmony_ci#include <sys/syscall.h>
7153a5a1b3Sopenharmony_ci#endif
7253a5a1b3Sopenharmony_ci
7353a5a1b3Sopenharmony_ci#include "oss-util.h"
7453a5a1b3Sopenharmony_ci
7553a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering");
7653a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("OSS Sink/Source");
7753a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
7853a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false);
7953a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
8053a5a1b3Sopenharmony_ci        "sink_name=<name for the sink> "
8153a5a1b3Sopenharmony_ci        "sink_properties=<properties for the sink> "
8253a5a1b3Sopenharmony_ci        "source_name=<name for the source> "
8353a5a1b3Sopenharmony_ci        "source_properties=<properties for the source> "
8453a5a1b3Sopenharmony_ci        "device=<OSS device> "
8553a5a1b3Sopenharmony_ci        "record=<enable source?> "
8653a5a1b3Sopenharmony_ci        "playback=<enable sink?> "
8753a5a1b3Sopenharmony_ci        "format=<sample format> "
8853a5a1b3Sopenharmony_ci        "rate=<sample rate> "
8953a5a1b3Sopenharmony_ci        "channels=<number of channels> "
9053a5a1b3Sopenharmony_ci        "channel_map=<channel map> "
9153a5a1b3Sopenharmony_ci        "fragments=<number of fragments> "
9253a5a1b3Sopenharmony_ci        "fragment_size=<fragment size> "
9353a5a1b3Sopenharmony_ci        "mmap=<enable memory mapping?>");
9453a5a1b3Sopenharmony_ci#ifdef __linux__
9553a5a1b3Sopenharmony_ciPA_MODULE_DEPRECATED("Please use module-alsa-card instead of module-oss!");
9653a5a1b3Sopenharmony_ci#endif
9753a5a1b3Sopenharmony_ci
9853a5a1b3Sopenharmony_ci#define DEFAULT_DEVICE "/dev/dsp"
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_cistruct userdata {
10153a5a1b3Sopenharmony_ci    pa_core *core;
10253a5a1b3Sopenharmony_ci    pa_module *module;
10353a5a1b3Sopenharmony_ci    pa_sink *sink;
10453a5a1b3Sopenharmony_ci    pa_source *source;
10553a5a1b3Sopenharmony_ci
10653a5a1b3Sopenharmony_ci    pa_thread *thread;
10753a5a1b3Sopenharmony_ci    pa_thread_mq thread_mq;
10853a5a1b3Sopenharmony_ci    pa_rtpoll *rtpoll;
10953a5a1b3Sopenharmony_ci
11053a5a1b3Sopenharmony_ci    char *device_name;
11153a5a1b3Sopenharmony_ci
11253a5a1b3Sopenharmony_ci    pa_memchunk memchunk;
11353a5a1b3Sopenharmony_ci
11453a5a1b3Sopenharmony_ci    size_t frame_size;
11553a5a1b3Sopenharmony_ci    uint32_t in_fragment_size, out_fragment_size, in_nfrags, out_nfrags, in_hwbuf_size, out_hwbuf_size;
11653a5a1b3Sopenharmony_ci    bool use_getospace, use_getispace;
11753a5a1b3Sopenharmony_ci    bool use_getodelay;
11853a5a1b3Sopenharmony_ci
11953a5a1b3Sopenharmony_ci    bool sink_suspended, source_suspended;
12053a5a1b3Sopenharmony_ci
12153a5a1b3Sopenharmony_ci    int fd;
12253a5a1b3Sopenharmony_ci    int mode;
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci    int mixer_fd;
12553a5a1b3Sopenharmony_ci    int mixer_devmask;
12653a5a1b3Sopenharmony_ci
12753a5a1b3Sopenharmony_ci    int nfrags, frag_size, orig_frag_size;
12853a5a1b3Sopenharmony_ci
12953a5a1b3Sopenharmony_ci    bool shutdown;
13053a5a1b3Sopenharmony_ci
13153a5a1b3Sopenharmony_ci    bool use_mmap;
13253a5a1b3Sopenharmony_ci    unsigned out_mmap_current, in_mmap_current;
13353a5a1b3Sopenharmony_ci    void *in_mmap, *out_mmap;
13453a5a1b3Sopenharmony_ci    pa_memblock **in_mmap_memblocks, **out_mmap_memblocks;
13553a5a1b3Sopenharmony_ci
13653a5a1b3Sopenharmony_ci    int in_mmap_saved_nfrags, out_mmap_saved_nfrags;
13753a5a1b3Sopenharmony_ci
13853a5a1b3Sopenharmony_ci    pa_rtpoll_item *rtpoll_item;
13953a5a1b3Sopenharmony_ci};
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = {
14253a5a1b3Sopenharmony_ci    "sink_name",
14353a5a1b3Sopenharmony_ci    "sink_properties",
14453a5a1b3Sopenharmony_ci    "source_name",
14553a5a1b3Sopenharmony_ci    "source_properties",
14653a5a1b3Sopenharmony_ci    "device",
14753a5a1b3Sopenharmony_ci    "record",
14853a5a1b3Sopenharmony_ci    "playback",
14953a5a1b3Sopenharmony_ci    "fragments",
15053a5a1b3Sopenharmony_ci    "fragment_size",
15153a5a1b3Sopenharmony_ci    "format",
15253a5a1b3Sopenharmony_ci    "rate",
15353a5a1b3Sopenharmony_ci    "channels",
15453a5a1b3Sopenharmony_ci    "channel_map",
15553a5a1b3Sopenharmony_ci    "mmap",
15653a5a1b3Sopenharmony_ci    NULL
15753a5a1b3Sopenharmony_ci};
15853a5a1b3Sopenharmony_ci
15953a5a1b3Sopenharmony_ci/* Sink and source states are passed as arguments, because this is called
16053a5a1b3Sopenharmony_ci * during state changes, and we need the new state, but thread_info.state
16153a5a1b3Sopenharmony_ci * has not yet been updated. */
16253a5a1b3Sopenharmony_cistatic void trigger(struct userdata *u, pa_sink_state_t sink_state, pa_source_state_t source_state, bool quick) {
16353a5a1b3Sopenharmony_ci    int enable_bits = 0, zero = 0;
16453a5a1b3Sopenharmony_ci
16553a5a1b3Sopenharmony_ci    pa_assert(u);
16653a5a1b3Sopenharmony_ci
16753a5a1b3Sopenharmony_ci    if (u->fd < 0)
16853a5a1b3Sopenharmony_ci        return;
16953a5a1b3Sopenharmony_ci
17053a5a1b3Sopenharmony_ci    pa_log_debug("trigger");
17153a5a1b3Sopenharmony_ci
17253a5a1b3Sopenharmony_ci    if (u->source && PA_SOURCE_IS_OPENED(source_state))
17353a5a1b3Sopenharmony_ci        enable_bits |= PCM_ENABLE_INPUT;
17453a5a1b3Sopenharmony_ci
17553a5a1b3Sopenharmony_ci    if (u->sink && PA_SINK_IS_OPENED(sink_state))
17653a5a1b3Sopenharmony_ci        enable_bits |= PCM_ENABLE_OUTPUT;
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_ci    pa_log_debug("trigger: %i", enable_bits);
17953a5a1b3Sopenharmony_ci
18053a5a1b3Sopenharmony_ci    if (u->use_mmap) {
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_ci        if (!quick)
18353a5a1b3Sopenharmony_ci            ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero);
18453a5a1b3Sopenharmony_ci
18553a5a1b3Sopenharmony_ci#ifdef SNDCTL_DSP_HALT
18653a5a1b3Sopenharmony_ci        if (enable_bits == 0)
18753a5a1b3Sopenharmony_ci            if (ioctl(u->fd, SNDCTL_DSP_HALT, NULL) < 0)
18853a5a1b3Sopenharmony_ci                pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno));
18953a5a1b3Sopenharmony_ci#endif
19053a5a1b3Sopenharmony_ci
19153a5a1b3Sopenharmony_ci        if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0)
19253a5a1b3Sopenharmony_ci            pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno));
19353a5a1b3Sopenharmony_ci
19453a5a1b3Sopenharmony_ci        if (u->sink && !(enable_bits & PCM_ENABLE_OUTPUT)) {
19553a5a1b3Sopenharmony_ci            pa_log_debug("clearing playback buffer");
19653a5a1b3Sopenharmony_ci            pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &u->sink->sample_spec);
19753a5a1b3Sopenharmony_ci        }
19853a5a1b3Sopenharmony_ci
19953a5a1b3Sopenharmony_ci    } else {
20053a5a1b3Sopenharmony_ci
20153a5a1b3Sopenharmony_ci        if (enable_bits)
20253a5a1b3Sopenharmony_ci            if (ioctl(u->fd, SNDCTL_DSP_POST, NULL) < 0)
20353a5a1b3Sopenharmony_ci                pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno));
20453a5a1b3Sopenharmony_ci
20553a5a1b3Sopenharmony_ci        if (!quick) {
20653a5a1b3Sopenharmony_ci            /*
20753a5a1b3Sopenharmony_ci             * Some crappy drivers do not start the recording until we
20853a5a1b3Sopenharmony_ci             * read something.  Without this snippet, poll will never
20953a5a1b3Sopenharmony_ci             * register the fd as ready.
21053a5a1b3Sopenharmony_ci             */
21153a5a1b3Sopenharmony_ci
21253a5a1b3Sopenharmony_ci            if (u->source && PA_SOURCE_IS_OPENED(source_state)) {
21353a5a1b3Sopenharmony_ci                uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);
21453a5a1b3Sopenharmony_ci
21553a5a1b3Sopenharmony_ci                /* XXX: Shouldn't this be done only when resuming the source?
21653a5a1b3Sopenharmony_ci                 * Currently this code path is executed also when resuming the
21753a5a1b3Sopenharmony_ci                 * sink while the source is already running. */
21853a5a1b3Sopenharmony_ci
21953a5a1b3Sopenharmony_ci                if (pa_read(u->fd, buf, u->in_fragment_size, NULL) < 0)
22053a5a1b3Sopenharmony_ci                    pa_log("pa_read() failed: %s", pa_cstrerror(errno));
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_ci                pa_xfree(buf);
22353a5a1b3Sopenharmony_ci            }
22453a5a1b3Sopenharmony_ci        }
22553a5a1b3Sopenharmony_ci    }
22653a5a1b3Sopenharmony_ci}
22753a5a1b3Sopenharmony_ci
22853a5a1b3Sopenharmony_cistatic void mmap_fill_memblocks(struct userdata *u, unsigned n) {
22953a5a1b3Sopenharmony_ci    pa_assert(u);
23053a5a1b3Sopenharmony_ci    pa_assert(u->out_mmap_memblocks);
23153a5a1b3Sopenharmony_ci
23253a5a1b3Sopenharmony_ci/*     pa_log("Mmmap writing %u blocks", n); */
23353a5a1b3Sopenharmony_ci
23453a5a1b3Sopenharmony_ci    while (n > 0) {
23553a5a1b3Sopenharmony_ci        pa_memchunk chunk;
23653a5a1b3Sopenharmony_ci
23753a5a1b3Sopenharmony_ci        if (u->out_mmap_memblocks[u->out_mmap_current])
23853a5a1b3Sopenharmony_ci            pa_memblock_unref_fixed(u->out_mmap_memblocks[u->out_mmap_current]);
23953a5a1b3Sopenharmony_ci
24053a5a1b3Sopenharmony_ci        chunk.memblock = u->out_mmap_memblocks[u->out_mmap_current] =
24153a5a1b3Sopenharmony_ci            pa_memblock_new_fixed(
24253a5a1b3Sopenharmony_ci                    u->core->mempool,
24353a5a1b3Sopenharmony_ci                    (uint8_t*) u->out_mmap + u->out_fragment_size * u->out_mmap_current,
24453a5a1b3Sopenharmony_ci                    u->out_fragment_size,
24553a5a1b3Sopenharmony_ci                    1);
24653a5a1b3Sopenharmony_ci
24753a5a1b3Sopenharmony_ci        chunk.length = pa_memblock_get_length(chunk.memblock);
24853a5a1b3Sopenharmony_ci        chunk.index = 0;
24953a5a1b3Sopenharmony_ci
25053a5a1b3Sopenharmony_ci        pa_sink_render_into_full(u->sink, &chunk);
25153a5a1b3Sopenharmony_ci
25253a5a1b3Sopenharmony_ci        u->out_mmap_current++;
25353a5a1b3Sopenharmony_ci        while (u->out_mmap_current >= u->out_nfrags)
25453a5a1b3Sopenharmony_ci            u->out_mmap_current -= u->out_nfrags;
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_ci        n--;
25753a5a1b3Sopenharmony_ci    }
25853a5a1b3Sopenharmony_ci}
25953a5a1b3Sopenharmony_ci
26053a5a1b3Sopenharmony_cistatic int mmap_write(struct userdata *u) {
26153a5a1b3Sopenharmony_ci    struct count_info info;
26253a5a1b3Sopenharmony_ci
26353a5a1b3Sopenharmony_ci    pa_assert(u);
26453a5a1b3Sopenharmony_ci    pa_assert(u->sink);
26553a5a1b3Sopenharmony_ci
26653a5a1b3Sopenharmony_ci/*     pa_log("Mmmap writing..."); */
26753a5a1b3Sopenharmony_ci
26853a5a1b3Sopenharmony_ci    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
26953a5a1b3Sopenharmony_ci        pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
27053a5a1b3Sopenharmony_ci        return -1;
27153a5a1b3Sopenharmony_ci    }
27253a5a1b3Sopenharmony_ci
27353a5a1b3Sopenharmony_ci    info.blocks += u->out_mmap_saved_nfrags;
27453a5a1b3Sopenharmony_ci    u->out_mmap_saved_nfrags = 0;
27553a5a1b3Sopenharmony_ci
27653a5a1b3Sopenharmony_ci    if (info.blocks > 0)
27753a5a1b3Sopenharmony_ci        mmap_fill_memblocks(u, (unsigned) info.blocks);
27853a5a1b3Sopenharmony_ci
27953a5a1b3Sopenharmony_ci    return info.blocks;
28053a5a1b3Sopenharmony_ci}
28153a5a1b3Sopenharmony_ci
28253a5a1b3Sopenharmony_cistatic void mmap_post_memblocks(struct userdata *u, unsigned n) {
28353a5a1b3Sopenharmony_ci    pa_assert(u);
28453a5a1b3Sopenharmony_ci    pa_assert(u->in_mmap_memblocks);
28553a5a1b3Sopenharmony_ci
28653a5a1b3Sopenharmony_ci/*     pa_log("Mmmap reading %u blocks", n); */
28753a5a1b3Sopenharmony_ci
28853a5a1b3Sopenharmony_ci    while (n > 0) {
28953a5a1b3Sopenharmony_ci        pa_memchunk chunk;
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_ci        if (!u->in_mmap_memblocks[u->in_mmap_current]) {
29253a5a1b3Sopenharmony_ci
29353a5a1b3Sopenharmony_ci            chunk.memblock = u->in_mmap_memblocks[u->in_mmap_current] =
29453a5a1b3Sopenharmony_ci                pa_memblock_new_fixed(
29553a5a1b3Sopenharmony_ci                        u->core->mempool,
29653a5a1b3Sopenharmony_ci                        (uint8_t*) u->in_mmap + u->in_fragment_size*u->in_mmap_current,
29753a5a1b3Sopenharmony_ci                        u->in_fragment_size,
29853a5a1b3Sopenharmony_ci                        1);
29953a5a1b3Sopenharmony_ci
30053a5a1b3Sopenharmony_ci            chunk.length = pa_memblock_get_length(chunk.memblock);
30153a5a1b3Sopenharmony_ci            chunk.index = 0;
30253a5a1b3Sopenharmony_ci
30353a5a1b3Sopenharmony_ci            pa_source_post(u->source, &chunk);
30453a5a1b3Sopenharmony_ci        }
30553a5a1b3Sopenharmony_ci
30653a5a1b3Sopenharmony_ci        u->in_mmap_current++;
30753a5a1b3Sopenharmony_ci        while (u->in_mmap_current >= u->in_nfrags)
30853a5a1b3Sopenharmony_ci            u->in_mmap_current -= u->in_nfrags;
30953a5a1b3Sopenharmony_ci
31053a5a1b3Sopenharmony_ci        n--;
31153a5a1b3Sopenharmony_ci    }
31253a5a1b3Sopenharmony_ci}
31353a5a1b3Sopenharmony_ci
31453a5a1b3Sopenharmony_cistatic void mmap_clear_memblocks(struct userdata*u, unsigned n) {
31553a5a1b3Sopenharmony_ci    unsigned i = u->in_mmap_current;
31653a5a1b3Sopenharmony_ci
31753a5a1b3Sopenharmony_ci    pa_assert(u);
31853a5a1b3Sopenharmony_ci    pa_assert(u->in_mmap_memblocks);
31953a5a1b3Sopenharmony_ci
32053a5a1b3Sopenharmony_ci    if (n > u->in_nfrags)
32153a5a1b3Sopenharmony_ci        n = u->in_nfrags;
32253a5a1b3Sopenharmony_ci
32353a5a1b3Sopenharmony_ci    while (n > 0) {
32453a5a1b3Sopenharmony_ci        if (u->in_mmap_memblocks[i]) {
32553a5a1b3Sopenharmony_ci            pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
32653a5a1b3Sopenharmony_ci            u->in_mmap_memblocks[i] = NULL;
32753a5a1b3Sopenharmony_ci        }
32853a5a1b3Sopenharmony_ci
32953a5a1b3Sopenharmony_ci        i++;
33053a5a1b3Sopenharmony_ci        while (i >= u->in_nfrags)
33153a5a1b3Sopenharmony_ci            i -= u->in_nfrags;
33253a5a1b3Sopenharmony_ci
33353a5a1b3Sopenharmony_ci        n--;
33453a5a1b3Sopenharmony_ci    }
33553a5a1b3Sopenharmony_ci}
33653a5a1b3Sopenharmony_ci
33753a5a1b3Sopenharmony_cistatic int mmap_read(struct userdata *u) {
33853a5a1b3Sopenharmony_ci    struct count_info info;
33953a5a1b3Sopenharmony_ci    pa_assert(u);
34053a5a1b3Sopenharmony_ci    pa_assert(u->source);
34153a5a1b3Sopenharmony_ci
34253a5a1b3Sopenharmony_ci/*     pa_log("Mmmap reading..."); */
34353a5a1b3Sopenharmony_ci
34453a5a1b3Sopenharmony_ci    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
34553a5a1b3Sopenharmony_ci        pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
34653a5a1b3Sopenharmony_ci        return -1;
34753a5a1b3Sopenharmony_ci    }
34853a5a1b3Sopenharmony_ci
34953a5a1b3Sopenharmony_ci/*     pa_log("... %i", info.blocks); */
35053a5a1b3Sopenharmony_ci
35153a5a1b3Sopenharmony_ci    info.blocks += u->in_mmap_saved_nfrags;
35253a5a1b3Sopenharmony_ci    u->in_mmap_saved_nfrags = 0;
35353a5a1b3Sopenharmony_ci
35453a5a1b3Sopenharmony_ci    if (info.blocks > 0) {
35553a5a1b3Sopenharmony_ci        mmap_post_memblocks(u, (unsigned) info.blocks);
35653a5a1b3Sopenharmony_ci        mmap_clear_memblocks(u, u->in_nfrags/2);
35753a5a1b3Sopenharmony_ci    }
35853a5a1b3Sopenharmony_ci
35953a5a1b3Sopenharmony_ci    return info.blocks;
36053a5a1b3Sopenharmony_ci}
36153a5a1b3Sopenharmony_ci
36253a5a1b3Sopenharmony_cistatic pa_usec_t mmap_sink_get_latency(struct userdata *u) {
36353a5a1b3Sopenharmony_ci    struct count_info info;
36453a5a1b3Sopenharmony_ci    size_t bpos, n;
36553a5a1b3Sopenharmony_ci
36653a5a1b3Sopenharmony_ci    pa_assert(u);
36753a5a1b3Sopenharmony_ci
36853a5a1b3Sopenharmony_ci    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
36953a5a1b3Sopenharmony_ci        pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
37053a5a1b3Sopenharmony_ci        return 0;
37153a5a1b3Sopenharmony_ci    }
37253a5a1b3Sopenharmony_ci
37353a5a1b3Sopenharmony_ci    u->out_mmap_saved_nfrags += info.blocks;
37453a5a1b3Sopenharmony_ci
37553a5a1b3Sopenharmony_ci    bpos = ((u->out_mmap_current + (unsigned) u->out_mmap_saved_nfrags) * u->out_fragment_size) % u->out_hwbuf_size;
37653a5a1b3Sopenharmony_ci
37753a5a1b3Sopenharmony_ci    if (bpos <= (size_t) info.ptr)
37853a5a1b3Sopenharmony_ci        n = u->out_hwbuf_size - ((size_t) info.ptr - bpos);
37953a5a1b3Sopenharmony_ci    else
38053a5a1b3Sopenharmony_ci        n = bpos - (size_t) info.ptr;
38153a5a1b3Sopenharmony_ci
38253a5a1b3Sopenharmony_ci/*     pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->out_fragment_size, u->out_fragments); */
38353a5a1b3Sopenharmony_ci
38453a5a1b3Sopenharmony_ci    return pa_bytes_to_usec(n, &u->sink->sample_spec);
38553a5a1b3Sopenharmony_ci}
38653a5a1b3Sopenharmony_ci
38753a5a1b3Sopenharmony_cistatic pa_usec_t mmap_source_get_latency(struct userdata *u) {
38853a5a1b3Sopenharmony_ci    struct count_info info;
38953a5a1b3Sopenharmony_ci    size_t bpos, n;
39053a5a1b3Sopenharmony_ci
39153a5a1b3Sopenharmony_ci    pa_assert(u);
39253a5a1b3Sopenharmony_ci
39353a5a1b3Sopenharmony_ci    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
39453a5a1b3Sopenharmony_ci        pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
39553a5a1b3Sopenharmony_ci        return 0;
39653a5a1b3Sopenharmony_ci    }
39753a5a1b3Sopenharmony_ci
39853a5a1b3Sopenharmony_ci    u->in_mmap_saved_nfrags += info.blocks;
39953a5a1b3Sopenharmony_ci    bpos = ((u->in_mmap_current + (unsigned) u->in_mmap_saved_nfrags) * u->in_fragment_size) % u->in_hwbuf_size;
40053a5a1b3Sopenharmony_ci
40153a5a1b3Sopenharmony_ci    if (bpos <= (size_t) info.ptr)
40253a5a1b3Sopenharmony_ci        n = (size_t) info.ptr - bpos;
40353a5a1b3Sopenharmony_ci    else
40453a5a1b3Sopenharmony_ci        n = u->in_hwbuf_size - bpos + (size_t) info.ptr;
40553a5a1b3Sopenharmony_ci
40653a5a1b3Sopenharmony_ci/*     pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->in_fragment_size, u->in_fragments);  */
40753a5a1b3Sopenharmony_ci
40853a5a1b3Sopenharmony_ci    return pa_bytes_to_usec(n, &u->source->sample_spec);
40953a5a1b3Sopenharmony_ci}
41053a5a1b3Sopenharmony_ci
41153a5a1b3Sopenharmony_cistatic pa_usec_t io_sink_get_latency(struct userdata *u) {
41253a5a1b3Sopenharmony_ci    pa_usec_t r = 0;
41353a5a1b3Sopenharmony_ci
41453a5a1b3Sopenharmony_ci    pa_assert(u);
41553a5a1b3Sopenharmony_ci
41653a5a1b3Sopenharmony_ci    if (u->use_getodelay) {
41753a5a1b3Sopenharmony_ci        int arg;
41853a5a1b3Sopenharmony_ci#if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
41953a5a1b3Sopenharmony_ci#if defined(AUDIO_GETBUFINFO)
42053a5a1b3Sopenharmony_ci        struct audio_info info;
42153a5a1b3Sopenharmony_ci        if (syscall(SYS_ioctl, u->fd, AUDIO_GETBUFINFO, &info) < 0) {
42253a5a1b3Sopenharmony_ci            pa_log_info("Device doesn't support AUDIO_GETBUFINFO: %s", pa_cstrerror(errno));
42353a5a1b3Sopenharmony_ci            u->use_getodelay = 0;
42453a5a1b3Sopenharmony_ci        } else {
42553a5a1b3Sopenharmony_ci            arg = info.play.seek + info.blocksize / 2;
42653a5a1b3Sopenharmony_ci            r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);
42753a5a1b3Sopenharmony_ci        }
42853a5a1b3Sopenharmony_ci#else
42953a5a1b3Sopenharmony_ci        pa_log_info("System doesn't support AUDIO_GETBUFINFO");
43053a5a1b3Sopenharmony_ci        u->use_getodelay = 0;
43153a5a1b3Sopenharmony_ci#endif
43253a5a1b3Sopenharmony_ci#else
43353a5a1b3Sopenharmony_ci        if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
43453a5a1b3Sopenharmony_ci            pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno));
43553a5a1b3Sopenharmony_ci            u->use_getodelay = 0;
43653a5a1b3Sopenharmony_ci        } else
43753a5a1b3Sopenharmony_ci            r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);
43853a5a1b3Sopenharmony_ci#endif
43953a5a1b3Sopenharmony_ci    }
44053a5a1b3Sopenharmony_ci
44153a5a1b3Sopenharmony_ci    if (!u->use_getodelay && u->use_getospace) {
44253a5a1b3Sopenharmony_ci        struct audio_buf_info info;
44353a5a1b3Sopenharmony_ci
44453a5a1b3Sopenharmony_ci        if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
44553a5a1b3Sopenharmony_ci            pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
44653a5a1b3Sopenharmony_ci            u->use_getospace = 0;
44753a5a1b3Sopenharmony_ci        } else
44853a5a1b3Sopenharmony_ci            r = pa_bytes_to_usec((size_t) info.bytes, &u->sink->sample_spec);
44953a5a1b3Sopenharmony_ci    }
45053a5a1b3Sopenharmony_ci
45153a5a1b3Sopenharmony_ci    if (u->memchunk.memblock)
45253a5a1b3Sopenharmony_ci        r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
45353a5a1b3Sopenharmony_ci
45453a5a1b3Sopenharmony_ci    return r;
45553a5a1b3Sopenharmony_ci}
45653a5a1b3Sopenharmony_ci
45753a5a1b3Sopenharmony_cistatic pa_usec_t io_source_get_latency(struct userdata *u) {
45853a5a1b3Sopenharmony_ci    pa_usec_t r = 0;
45953a5a1b3Sopenharmony_ci
46053a5a1b3Sopenharmony_ci    pa_assert(u);
46153a5a1b3Sopenharmony_ci
46253a5a1b3Sopenharmony_ci    if (u->use_getispace) {
46353a5a1b3Sopenharmony_ci        struct audio_buf_info info;
46453a5a1b3Sopenharmony_ci
46553a5a1b3Sopenharmony_ci        if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
46653a5a1b3Sopenharmony_ci            pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
46753a5a1b3Sopenharmony_ci            u->use_getispace = 0;
46853a5a1b3Sopenharmony_ci        } else
46953a5a1b3Sopenharmony_ci            r = pa_bytes_to_usec((size_t) info.bytes, &u->source->sample_spec);
47053a5a1b3Sopenharmony_ci    }
47153a5a1b3Sopenharmony_ci
47253a5a1b3Sopenharmony_ci    return r;
47353a5a1b3Sopenharmony_ci}
47453a5a1b3Sopenharmony_ci
47553a5a1b3Sopenharmony_cistatic void build_pollfd(struct userdata *u) {
47653a5a1b3Sopenharmony_ci    struct pollfd *pollfd;
47753a5a1b3Sopenharmony_ci
47853a5a1b3Sopenharmony_ci    pa_assert(u);
47953a5a1b3Sopenharmony_ci    pa_assert(u->fd >= 0);
48053a5a1b3Sopenharmony_ci
48153a5a1b3Sopenharmony_ci    if (u->rtpoll_item)
48253a5a1b3Sopenharmony_ci        pa_rtpoll_item_free(u->rtpoll_item);
48353a5a1b3Sopenharmony_ci
48453a5a1b3Sopenharmony_ci    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
48553a5a1b3Sopenharmony_ci    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
48653a5a1b3Sopenharmony_ci    pollfd->fd = u->fd;
48753a5a1b3Sopenharmony_ci    pollfd->events = 0;
48853a5a1b3Sopenharmony_ci    pollfd->revents = 0;
48953a5a1b3Sopenharmony_ci}
49053a5a1b3Sopenharmony_ci
49153a5a1b3Sopenharmony_ci/* Called from IO context */
49253a5a1b3Sopenharmony_cistatic void suspend(struct userdata *u) {
49353a5a1b3Sopenharmony_ci    pa_assert(u);
49453a5a1b3Sopenharmony_ci    pa_assert(u->fd >= 0);
49553a5a1b3Sopenharmony_ci
49653a5a1b3Sopenharmony_ci    pa_log_info("Suspending...");
49753a5a1b3Sopenharmony_ci
49853a5a1b3Sopenharmony_ci    if (u->out_mmap_memblocks) {
49953a5a1b3Sopenharmony_ci        unsigned i;
50053a5a1b3Sopenharmony_ci        for (i = 0; i < u->out_nfrags; i++)
50153a5a1b3Sopenharmony_ci            if (u->out_mmap_memblocks[i]) {
50253a5a1b3Sopenharmony_ci                pa_memblock_unref_fixed(u->out_mmap_memblocks[i]);
50353a5a1b3Sopenharmony_ci                u->out_mmap_memblocks[i] = NULL;
50453a5a1b3Sopenharmony_ci            }
50553a5a1b3Sopenharmony_ci    }
50653a5a1b3Sopenharmony_ci
50753a5a1b3Sopenharmony_ci    if (u->in_mmap_memblocks) {
50853a5a1b3Sopenharmony_ci        unsigned i;
50953a5a1b3Sopenharmony_ci        for (i = 0; i < u->in_nfrags; i++)
51053a5a1b3Sopenharmony_ci            if (u->in_mmap_memblocks[i]) {
51153a5a1b3Sopenharmony_ci                pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
51253a5a1b3Sopenharmony_ci                u->in_mmap_memblocks[i] = NULL;
51353a5a1b3Sopenharmony_ci            }
51453a5a1b3Sopenharmony_ci    }
51553a5a1b3Sopenharmony_ci
51653a5a1b3Sopenharmony_ci    if (u->in_mmap && u->in_mmap != MAP_FAILED) {
51753a5a1b3Sopenharmony_ci        munmap(u->in_mmap, u->in_hwbuf_size);
51853a5a1b3Sopenharmony_ci        u->in_mmap = NULL;
51953a5a1b3Sopenharmony_ci    }
52053a5a1b3Sopenharmony_ci
52153a5a1b3Sopenharmony_ci    if (u->out_mmap && u->out_mmap != MAP_FAILED) {
52253a5a1b3Sopenharmony_ci        munmap(u->out_mmap, u->out_hwbuf_size);
52353a5a1b3Sopenharmony_ci        u->out_mmap = NULL;
52453a5a1b3Sopenharmony_ci    }
52553a5a1b3Sopenharmony_ci
52653a5a1b3Sopenharmony_ci    /* Let's suspend */
52753a5a1b3Sopenharmony_ci    ioctl(u->fd, SNDCTL_DSP_SYNC, NULL);
52853a5a1b3Sopenharmony_ci    pa_close(u->fd);
52953a5a1b3Sopenharmony_ci    u->fd = -1;
53053a5a1b3Sopenharmony_ci
53153a5a1b3Sopenharmony_ci    if (u->rtpoll_item) {
53253a5a1b3Sopenharmony_ci        pa_rtpoll_item_free(u->rtpoll_item);
53353a5a1b3Sopenharmony_ci        u->rtpoll_item = NULL;
53453a5a1b3Sopenharmony_ci    }
53553a5a1b3Sopenharmony_ci
53653a5a1b3Sopenharmony_ci    pa_log_info("Device suspended...");
53753a5a1b3Sopenharmony_ci}
53853a5a1b3Sopenharmony_ci
53953a5a1b3Sopenharmony_ci/* Called from IO context */
54053a5a1b3Sopenharmony_cistatic int unsuspend(struct userdata *u) {
54153a5a1b3Sopenharmony_ci    int m;
54253a5a1b3Sopenharmony_ci    pa_sample_spec ss, *ss_original;
54353a5a1b3Sopenharmony_ci    int frag_size, in_frag_size, out_frag_size;
54453a5a1b3Sopenharmony_ci    int in_nfrags, out_nfrags;
54553a5a1b3Sopenharmony_ci    struct audio_buf_info info;
54653a5a1b3Sopenharmony_ci
54753a5a1b3Sopenharmony_ci    pa_assert(u);
54853a5a1b3Sopenharmony_ci    pa_assert(u->fd < 0);
54953a5a1b3Sopenharmony_ci
55053a5a1b3Sopenharmony_ci    m = u->mode;
55153a5a1b3Sopenharmony_ci
55253a5a1b3Sopenharmony_ci    pa_log_info("Trying resume...");
55353a5a1b3Sopenharmony_ci
55453a5a1b3Sopenharmony_ci    if ((u->fd = pa_oss_open(u->device_name, &m, NULL)) < 0) {
55553a5a1b3Sopenharmony_ci        pa_log_warn("Resume failed, device busy (%s)", pa_cstrerror(errno));
55653a5a1b3Sopenharmony_ci        return -1;
55753a5a1b3Sopenharmony_ci    }
55853a5a1b3Sopenharmony_ci
55953a5a1b3Sopenharmony_ci    if (m != u->mode) {
56053a5a1b3Sopenharmony_ci        pa_log_warn("Resume failed, couldn't open device with original access mode.");
56153a5a1b3Sopenharmony_ci        goto fail;
56253a5a1b3Sopenharmony_ci    }
56353a5a1b3Sopenharmony_ci
56453a5a1b3Sopenharmony_ci    if (u->nfrags >= 2 && u->frag_size >= 1)
56553a5a1b3Sopenharmony_ci        if (pa_oss_set_fragments(u->fd, u->nfrags, u->orig_frag_size) < 0) {
56653a5a1b3Sopenharmony_ci            pa_log_warn("Resume failed, couldn't set original fragment settings.");
56753a5a1b3Sopenharmony_ci            goto fail;
56853a5a1b3Sopenharmony_ci        }
56953a5a1b3Sopenharmony_ci
57053a5a1b3Sopenharmony_ci    ss = *(ss_original = u->sink ? &u->sink->sample_spec : &u->source->sample_spec);
57153a5a1b3Sopenharmony_ci    if (pa_oss_auto_format(u->fd, &ss) < 0 || !pa_sample_spec_equal(&ss, ss_original)) {
57253a5a1b3Sopenharmony_ci        pa_log_warn("Resume failed, couldn't set original sample format settings.");
57353a5a1b3Sopenharmony_ci        goto fail;
57453a5a1b3Sopenharmony_ci    }
57553a5a1b3Sopenharmony_ci
57653a5a1b3Sopenharmony_ci    if (ioctl(u->fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
57753a5a1b3Sopenharmony_ci        pa_log_warn("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno));
57853a5a1b3Sopenharmony_ci        goto fail;
57953a5a1b3Sopenharmony_ci    }
58053a5a1b3Sopenharmony_ci
58153a5a1b3Sopenharmony_ci    in_frag_size = out_frag_size = frag_size;
58253a5a1b3Sopenharmony_ci    in_nfrags = out_nfrags = u->nfrags;
58353a5a1b3Sopenharmony_ci
58453a5a1b3Sopenharmony_ci    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
58553a5a1b3Sopenharmony_ci        in_frag_size = info.fragsize;
58653a5a1b3Sopenharmony_ci        in_nfrags = info.fragstotal;
58753a5a1b3Sopenharmony_ci    }
58853a5a1b3Sopenharmony_ci
58953a5a1b3Sopenharmony_ci    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
59053a5a1b3Sopenharmony_ci        out_frag_size = info.fragsize;
59153a5a1b3Sopenharmony_ci        out_nfrags = info.fragstotal;
59253a5a1b3Sopenharmony_ci    }
59353a5a1b3Sopenharmony_ci
59453a5a1b3Sopenharmony_ci    if ((u->source && (in_frag_size != (int) u->in_fragment_size || in_nfrags != (int) u->in_nfrags)) ||
59553a5a1b3Sopenharmony_ci        (u->sink && (out_frag_size != (int) u->out_fragment_size || out_nfrags != (int) u->out_nfrags))) {
59653a5a1b3Sopenharmony_ci        pa_log_warn("Resume failed, input fragment settings don't match.");
59753a5a1b3Sopenharmony_ci        goto fail;
59853a5a1b3Sopenharmony_ci    }
59953a5a1b3Sopenharmony_ci
60053a5a1b3Sopenharmony_ci    if (u->use_mmap) {
60153a5a1b3Sopenharmony_ci        if (u->source) {
60253a5a1b3Sopenharmony_ci            if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
60353a5a1b3Sopenharmony_ci                pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno));
60453a5a1b3Sopenharmony_ci                goto fail;
60553a5a1b3Sopenharmony_ci            }
60653a5a1b3Sopenharmony_ci        }
60753a5a1b3Sopenharmony_ci
60853a5a1b3Sopenharmony_ci        if (u->sink) {
60953a5a1b3Sopenharmony_ci            if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
61053a5a1b3Sopenharmony_ci                pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno));
61153a5a1b3Sopenharmony_ci                if (u->in_mmap && u->in_mmap != MAP_FAILED) {
61253a5a1b3Sopenharmony_ci                    munmap(u->in_mmap, u->in_hwbuf_size);
61353a5a1b3Sopenharmony_ci                    u->in_mmap = NULL;
61453a5a1b3Sopenharmony_ci                }
61553a5a1b3Sopenharmony_ci
61653a5a1b3Sopenharmony_ci                goto fail;
61753a5a1b3Sopenharmony_ci            }
61853a5a1b3Sopenharmony_ci
61953a5a1b3Sopenharmony_ci            pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss);
62053a5a1b3Sopenharmony_ci        }
62153a5a1b3Sopenharmony_ci    }
62253a5a1b3Sopenharmony_ci
62353a5a1b3Sopenharmony_ci    u->out_mmap_current = u->in_mmap_current = 0;
62453a5a1b3Sopenharmony_ci    u->out_mmap_saved_nfrags = u->in_mmap_saved_nfrags = 0;
62553a5a1b3Sopenharmony_ci
62653a5a1b3Sopenharmony_ci    pa_assert(!u->rtpoll_item);
62753a5a1b3Sopenharmony_ci
62853a5a1b3Sopenharmony_ci    build_pollfd(u);
62953a5a1b3Sopenharmony_ci
63053a5a1b3Sopenharmony_ci    if (u->sink && u->sink->get_volume)
63153a5a1b3Sopenharmony_ci        u->sink->get_volume(u->sink);
63253a5a1b3Sopenharmony_ci    if (u->source && u->source->get_volume)
63353a5a1b3Sopenharmony_ci        u->source->get_volume(u->source);
63453a5a1b3Sopenharmony_ci
63553a5a1b3Sopenharmony_ci    pa_log_info("Resumed successfully...");
63653a5a1b3Sopenharmony_ci
63753a5a1b3Sopenharmony_ci    return 0;
63853a5a1b3Sopenharmony_ci
63953a5a1b3Sopenharmony_cifail:
64053a5a1b3Sopenharmony_ci    pa_close(u->fd);
64153a5a1b3Sopenharmony_ci    u->fd = -1;
64253a5a1b3Sopenharmony_ci    return -1;
64353a5a1b3Sopenharmony_ci}
64453a5a1b3Sopenharmony_ci
64553a5a1b3Sopenharmony_ci/* Called from IO context */
64653a5a1b3Sopenharmony_cistatic int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
64753a5a1b3Sopenharmony_ci    struct userdata *u = PA_SINK(o)->userdata;
64853a5a1b3Sopenharmony_ci
64953a5a1b3Sopenharmony_ci    switch (code) {
65053a5a1b3Sopenharmony_ci
65153a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_LATENCY: {
65253a5a1b3Sopenharmony_ci            pa_usec_t r = 0;
65353a5a1b3Sopenharmony_ci
65453a5a1b3Sopenharmony_ci            if (u->fd >= 0) {
65553a5a1b3Sopenharmony_ci                if (u->use_mmap)
65653a5a1b3Sopenharmony_ci                    r = mmap_sink_get_latency(u);
65753a5a1b3Sopenharmony_ci                else
65853a5a1b3Sopenharmony_ci                    r = io_sink_get_latency(u);
65953a5a1b3Sopenharmony_ci            }
66053a5a1b3Sopenharmony_ci
66153a5a1b3Sopenharmony_ci            *((int64_t*) data) = (int64_t)r;
66253a5a1b3Sopenharmony_ci
66353a5a1b3Sopenharmony_ci            return 0;
66453a5a1b3Sopenharmony_ci        }
66553a5a1b3Sopenharmony_ci    }
66653a5a1b3Sopenharmony_ci
66753a5a1b3Sopenharmony_ci    return pa_sink_process_msg(o, code, data, offset, chunk);
66853a5a1b3Sopenharmony_ci}
66953a5a1b3Sopenharmony_ci
67053a5a1b3Sopenharmony_ci/* Called from the IO thread. */
67153a5a1b3Sopenharmony_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) {
67253a5a1b3Sopenharmony_ci    struct userdata *u;
67353a5a1b3Sopenharmony_ci    bool do_trigger = false;
67453a5a1b3Sopenharmony_ci    bool quick = true;
67553a5a1b3Sopenharmony_ci
67653a5a1b3Sopenharmony_ci    pa_assert(s);
67753a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
67853a5a1b3Sopenharmony_ci
67953a5a1b3Sopenharmony_ci    /* It may be that only the suspend cause is changing, in which case there's
68053a5a1b3Sopenharmony_ci     * nothing to do. */
68153a5a1b3Sopenharmony_ci    if (new_state == s->thread_info.state)
68253a5a1b3Sopenharmony_ci        return 0;
68353a5a1b3Sopenharmony_ci
68453a5a1b3Sopenharmony_ci    switch (new_state) {
68553a5a1b3Sopenharmony_ci
68653a5a1b3Sopenharmony_ci        case PA_SINK_SUSPENDED:
68753a5a1b3Sopenharmony_ci            pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
68853a5a1b3Sopenharmony_ci
68953a5a1b3Sopenharmony_ci            if (!u->source || u->source_suspended)
69053a5a1b3Sopenharmony_ci                suspend(u);
69153a5a1b3Sopenharmony_ci
69253a5a1b3Sopenharmony_ci            do_trigger = true;
69353a5a1b3Sopenharmony_ci
69453a5a1b3Sopenharmony_ci            u->sink_suspended = true;
69553a5a1b3Sopenharmony_ci            break;
69653a5a1b3Sopenharmony_ci
69753a5a1b3Sopenharmony_ci        case PA_SINK_IDLE:
69853a5a1b3Sopenharmony_ci        case PA_SINK_RUNNING:
69953a5a1b3Sopenharmony_ci
70053a5a1b3Sopenharmony_ci            if (s->thread_info.state == PA_SINK_INIT) {
70153a5a1b3Sopenharmony_ci                do_trigger = true;
70253a5a1b3Sopenharmony_ci                quick = u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state);
70353a5a1b3Sopenharmony_ci            }
70453a5a1b3Sopenharmony_ci
70553a5a1b3Sopenharmony_ci            if (s->thread_info.state == PA_SINK_SUSPENDED) {
70653a5a1b3Sopenharmony_ci
70753a5a1b3Sopenharmony_ci                if (!u->source || u->source_suspended) {
70853a5a1b3Sopenharmony_ci                    if (unsuspend(u) < 0)
70953a5a1b3Sopenharmony_ci                        return -1;
71053a5a1b3Sopenharmony_ci                    quick = false;
71153a5a1b3Sopenharmony_ci                }
71253a5a1b3Sopenharmony_ci
71353a5a1b3Sopenharmony_ci                do_trigger = true;
71453a5a1b3Sopenharmony_ci
71553a5a1b3Sopenharmony_ci                u->out_mmap_current = 0;
71653a5a1b3Sopenharmony_ci                u->out_mmap_saved_nfrags = 0;
71753a5a1b3Sopenharmony_ci
71853a5a1b3Sopenharmony_ci                u->sink_suspended = false;
71953a5a1b3Sopenharmony_ci            }
72053a5a1b3Sopenharmony_ci
72153a5a1b3Sopenharmony_ci            break;
72253a5a1b3Sopenharmony_ci
72353a5a1b3Sopenharmony_ci        case PA_SINK_INVALID_STATE:
72453a5a1b3Sopenharmony_ci        case PA_SINK_UNLINKED:
72553a5a1b3Sopenharmony_ci        case PA_SINK_INIT:
72653a5a1b3Sopenharmony_ci            ;
72753a5a1b3Sopenharmony_ci    }
72853a5a1b3Sopenharmony_ci
72953a5a1b3Sopenharmony_ci    if (do_trigger)
73053a5a1b3Sopenharmony_ci        trigger(u, new_state, u->source ? u->source->thread_info.state : PA_SOURCE_INVALID_STATE, quick);
73153a5a1b3Sopenharmony_ci
73253a5a1b3Sopenharmony_ci    return 0;
73353a5a1b3Sopenharmony_ci}
73453a5a1b3Sopenharmony_ci
73553a5a1b3Sopenharmony_cistatic int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
73653a5a1b3Sopenharmony_ci    struct userdata *u = PA_SOURCE(o)->userdata;
73753a5a1b3Sopenharmony_ci
73853a5a1b3Sopenharmony_ci    switch (code) {
73953a5a1b3Sopenharmony_ci
74053a5a1b3Sopenharmony_ci        case PA_SOURCE_MESSAGE_GET_LATENCY: {
74153a5a1b3Sopenharmony_ci            pa_usec_t r = 0;
74253a5a1b3Sopenharmony_ci
74353a5a1b3Sopenharmony_ci            if (u->fd >= 0) {
74453a5a1b3Sopenharmony_ci                if (u->use_mmap)
74553a5a1b3Sopenharmony_ci                    r = mmap_source_get_latency(u);
74653a5a1b3Sopenharmony_ci                else
74753a5a1b3Sopenharmony_ci                    r = io_source_get_latency(u);
74853a5a1b3Sopenharmony_ci            }
74953a5a1b3Sopenharmony_ci
75053a5a1b3Sopenharmony_ci            *((int64_t*) data) = (int64_t)r;
75153a5a1b3Sopenharmony_ci            return 0;
75253a5a1b3Sopenharmony_ci        }
75353a5a1b3Sopenharmony_ci    }
75453a5a1b3Sopenharmony_ci
75553a5a1b3Sopenharmony_ci    return pa_source_process_msg(o, code, data, offset, chunk);
75653a5a1b3Sopenharmony_ci}
75753a5a1b3Sopenharmony_ci
75853a5a1b3Sopenharmony_ci/* Called from the IO thread. */
75953a5a1b3Sopenharmony_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) {
76053a5a1b3Sopenharmony_ci    struct userdata *u;
76153a5a1b3Sopenharmony_ci    bool do_trigger = false;
76253a5a1b3Sopenharmony_ci    bool quick = true;
76353a5a1b3Sopenharmony_ci
76453a5a1b3Sopenharmony_ci    pa_assert(s);
76553a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
76653a5a1b3Sopenharmony_ci
76753a5a1b3Sopenharmony_ci    /* It may be that only the suspend cause is changing, in which case there's
76853a5a1b3Sopenharmony_ci     * nothing to do. */
76953a5a1b3Sopenharmony_ci    if (new_state == s->thread_info.state)
77053a5a1b3Sopenharmony_ci        return 0;
77153a5a1b3Sopenharmony_ci
77253a5a1b3Sopenharmony_ci    switch (new_state) {
77353a5a1b3Sopenharmony_ci
77453a5a1b3Sopenharmony_ci        case PA_SOURCE_SUSPENDED:
77553a5a1b3Sopenharmony_ci            pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
77653a5a1b3Sopenharmony_ci
77753a5a1b3Sopenharmony_ci            if (!u->sink || u->sink_suspended)
77853a5a1b3Sopenharmony_ci                suspend(u);
77953a5a1b3Sopenharmony_ci
78053a5a1b3Sopenharmony_ci            do_trigger = true;
78153a5a1b3Sopenharmony_ci
78253a5a1b3Sopenharmony_ci            u->source_suspended = true;
78353a5a1b3Sopenharmony_ci            break;
78453a5a1b3Sopenharmony_ci
78553a5a1b3Sopenharmony_ci        case PA_SOURCE_IDLE:
78653a5a1b3Sopenharmony_ci        case PA_SOURCE_RUNNING:
78753a5a1b3Sopenharmony_ci
78853a5a1b3Sopenharmony_ci            if (s->thread_info.state == PA_SOURCE_INIT) {
78953a5a1b3Sopenharmony_ci                do_trigger = true;
79053a5a1b3Sopenharmony_ci                quick = u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state);
79153a5a1b3Sopenharmony_ci            }
79253a5a1b3Sopenharmony_ci
79353a5a1b3Sopenharmony_ci            if (s->thread_info.state == PA_SOURCE_SUSPENDED) {
79453a5a1b3Sopenharmony_ci
79553a5a1b3Sopenharmony_ci                if (!u->sink || u->sink_suspended) {
79653a5a1b3Sopenharmony_ci                    if (unsuspend(u) < 0)
79753a5a1b3Sopenharmony_ci                        return -1;
79853a5a1b3Sopenharmony_ci                    quick = false;
79953a5a1b3Sopenharmony_ci                }
80053a5a1b3Sopenharmony_ci
80153a5a1b3Sopenharmony_ci                do_trigger = true;
80253a5a1b3Sopenharmony_ci
80353a5a1b3Sopenharmony_ci                u->in_mmap_current = 0;
80453a5a1b3Sopenharmony_ci                u->in_mmap_saved_nfrags = 0;
80553a5a1b3Sopenharmony_ci
80653a5a1b3Sopenharmony_ci                u->source_suspended = false;
80753a5a1b3Sopenharmony_ci            }
80853a5a1b3Sopenharmony_ci            break;
80953a5a1b3Sopenharmony_ci
81053a5a1b3Sopenharmony_ci        case PA_SOURCE_UNLINKED:
81153a5a1b3Sopenharmony_ci        case PA_SOURCE_INIT:
81253a5a1b3Sopenharmony_ci        case PA_SOURCE_INVALID_STATE:
81353a5a1b3Sopenharmony_ci            ;
81453a5a1b3Sopenharmony_ci    }
81553a5a1b3Sopenharmony_ci
81653a5a1b3Sopenharmony_ci    if (do_trigger)
81753a5a1b3Sopenharmony_ci        trigger(u, u->sink ? u->sink->thread_info.state : PA_SINK_INVALID_STATE, new_state, quick);
81853a5a1b3Sopenharmony_ci
81953a5a1b3Sopenharmony_ci    return 0;
82053a5a1b3Sopenharmony_ci}
82153a5a1b3Sopenharmony_ci
82253a5a1b3Sopenharmony_cistatic void sink_get_volume(pa_sink *s) {
82353a5a1b3Sopenharmony_ci    struct userdata *u;
82453a5a1b3Sopenharmony_ci
82553a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
82653a5a1b3Sopenharmony_ci
82753a5a1b3Sopenharmony_ci    pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
82853a5a1b3Sopenharmony_ci
82953a5a1b3Sopenharmony_ci    if (u->mixer_devmask & SOUND_MASK_VOLUME)
83053a5a1b3Sopenharmony_ci        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->real_volume) >= 0)
83153a5a1b3Sopenharmony_ci            return;
83253a5a1b3Sopenharmony_ci
83353a5a1b3Sopenharmony_ci    if (u->mixer_devmask & SOUND_MASK_PCM)
83453a5a1b3Sopenharmony_ci        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->real_volume) >= 0)
83553a5a1b3Sopenharmony_ci            return;
83653a5a1b3Sopenharmony_ci
83753a5a1b3Sopenharmony_ci    pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
83853a5a1b3Sopenharmony_ci}
83953a5a1b3Sopenharmony_ci
84053a5a1b3Sopenharmony_cistatic void sink_set_volume(pa_sink *s) {
84153a5a1b3Sopenharmony_ci    struct userdata *u;
84253a5a1b3Sopenharmony_ci
84353a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
84453a5a1b3Sopenharmony_ci
84553a5a1b3Sopenharmony_ci    pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
84653a5a1b3Sopenharmony_ci
84753a5a1b3Sopenharmony_ci    if (u->mixer_devmask & SOUND_MASK_VOLUME)
84853a5a1b3Sopenharmony_ci        (void) pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->real_volume);
84953a5a1b3Sopenharmony_ci
85053a5a1b3Sopenharmony_ci    if (u->mixer_devmask & SOUND_MASK_PCM)
85153a5a1b3Sopenharmony_ci        (void) pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->real_volume);
85253a5a1b3Sopenharmony_ci}
85353a5a1b3Sopenharmony_ci
85453a5a1b3Sopenharmony_cistatic void source_get_volume(pa_source *s) {
85553a5a1b3Sopenharmony_ci    struct userdata *u;
85653a5a1b3Sopenharmony_ci
85753a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
85853a5a1b3Sopenharmony_ci
85953a5a1b3Sopenharmony_ci    pa_assert(u->mixer_devmask & (SOUND_MASK_MIC|SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
86053a5a1b3Sopenharmony_ci
86153a5a1b3Sopenharmony_ci    if (u->mixer_devmask & SOUND_MASK_IGAIN)
86253a5a1b3Sopenharmony_ci        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->real_volume) >= 0)
86353a5a1b3Sopenharmony_ci            return;
86453a5a1b3Sopenharmony_ci
86553a5a1b3Sopenharmony_ci    if (u->mixer_devmask & SOUND_MASK_RECLEV)
86653a5a1b3Sopenharmony_ci        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->real_volume) >= 0)
86753a5a1b3Sopenharmony_ci            return;
86853a5a1b3Sopenharmony_ci
86953a5a1b3Sopenharmony_ci    if (u->mixer_devmask & SOUND_MASK_MIC)
87053a5a1b3Sopenharmony_ci        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_MIC, &s->sample_spec, &s->real_volume) >= 0)
87153a5a1b3Sopenharmony_ci            return;
87253a5a1b3Sopenharmony_ci
87353a5a1b3Sopenharmony_ci    pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
87453a5a1b3Sopenharmony_ci}
87553a5a1b3Sopenharmony_ci
87653a5a1b3Sopenharmony_cistatic void source_set_volume(pa_source *s) {
87753a5a1b3Sopenharmony_ci    struct userdata *u;
87853a5a1b3Sopenharmony_ci
87953a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
88053a5a1b3Sopenharmony_ci
88153a5a1b3Sopenharmony_ci    pa_assert(u->mixer_devmask & (SOUND_MASK_MIC|SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
88253a5a1b3Sopenharmony_ci
88353a5a1b3Sopenharmony_ci    if (u->mixer_devmask & SOUND_MASK_IGAIN)
88453a5a1b3Sopenharmony_ci        (void) pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->real_volume);
88553a5a1b3Sopenharmony_ci
88653a5a1b3Sopenharmony_ci    if (u->mixer_devmask & SOUND_MASK_RECLEV)
88753a5a1b3Sopenharmony_ci        (void) pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->real_volume);
88853a5a1b3Sopenharmony_ci
88953a5a1b3Sopenharmony_ci    if (u->mixer_devmask & SOUND_MASK_MIC)
89053a5a1b3Sopenharmony_ci        (void) pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_MIC, &s->sample_spec, &s->real_volume);
89153a5a1b3Sopenharmony_ci}
89253a5a1b3Sopenharmony_ci
89353a5a1b3Sopenharmony_cistatic void thread_func(void *userdata) {
89453a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
89553a5a1b3Sopenharmony_ci    int write_type = 0, read_type = 0;
89653a5a1b3Sopenharmony_ci    short revents = 0;
89753a5a1b3Sopenharmony_ci
89853a5a1b3Sopenharmony_ci    pa_assert(u);
89953a5a1b3Sopenharmony_ci
90053a5a1b3Sopenharmony_ci    pa_log_debug("Thread starting up");
90153a5a1b3Sopenharmony_ci
90253a5a1b3Sopenharmony_ci    if (u->core->realtime_scheduling)
90353a5a1b3Sopenharmony_ci        pa_thread_make_realtime(u->core->realtime_priority);
90453a5a1b3Sopenharmony_ci
90553a5a1b3Sopenharmony_ci    pa_thread_mq_install(&u->thread_mq);
90653a5a1b3Sopenharmony_ci
90753a5a1b3Sopenharmony_ci    for (;;) {
90853a5a1b3Sopenharmony_ci        int ret;
90953a5a1b3Sopenharmony_ci
91053a5a1b3Sopenharmony_ci/*        pa_log("loop");    */
91153a5a1b3Sopenharmony_ci
91253a5a1b3Sopenharmony_ci        if (PA_UNLIKELY(u->sink && u->sink->thread_info.rewind_requested))
91353a5a1b3Sopenharmony_ci            pa_sink_process_rewind(u->sink, 0);
91453a5a1b3Sopenharmony_ci
91553a5a1b3Sopenharmony_ci        /* Render some data and write it to the dsp */
91653a5a1b3Sopenharmony_ci
91753a5a1b3Sopenharmony_ci        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
91853a5a1b3Sopenharmony_ci
91953a5a1b3Sopenharmony_ci            if (u->use_mmap) {
92053a5a1b3Sopenharmony_ci
92153a5a1b3Sopenharmony_ci                if ((ret = mmap_write(u)) < 0)
92253a5a1b3Sopenharmony_ci                    goto fail;
92353a5a1b3Sopenharmony_ci
92453a5a1b3Sopenharmony_ci                revents &= ~POLLOUT;
92553a5a1b3Sopenharmony_ci
92653a5a1b3Sopenharmony_ci                if (ret > 0)
92753a5a1b3Sopenharmony_ci                    continue;
92853a5a1b3Sopenharmony_ci
92953a5a1b3Sopenharmony_ci            } else {
93053a5a1b3Sopenharmony_ci                ssize_t l;
93153a5a1b3Sopenharmony_ci                bool loop = false, work_done = false;
93253a5a1b3Sopenharmony_ci
93353a5a1b3Sopenharmony_ci                l = (ssize_t) u->out_fragment_size;
93453a5a1b3Sopenharmony_ci
93553a5a1b3Sopenharmony_ci                if (u->use_getospace) {
93653a5a1b3Sopenharmony_ci                    audio_buf_info info;
93753a5a1b3Sopenharmony_ci
93853a5a1b3Sopenharmony_ci                    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
93953a5a1b3Sopenharmony_ci                        pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
94053a5a1b3Sopenharmony_ci                        u->use_getospace = false;
94153a5a1b3Sopenharmony_ci                    } else {
94253a5a1b3Sopenharmony_ci                        l = info.bytes;
94353a5a1b3Sopenharmony_ci
94453a5a1b3Sopenharmony_ci                        /* We loop only if GETOSPACE worked and we
94553a5a1b3Sopenharmony_ci                         * actually *know* that we can write more than
94653a5a1b3Sopenharmony_ci                         * one fragment at a time */
94753a5a1b3Sopenharmony_ci                        loop = true;
94853a5a1b3Sopenharmony_ci                    }
94953a5a1b3Sopenharmony_ci                }
95053a5a1b3Sopenharmony_ci
95153a5a1b3Sopenharmony_ci                /* Round down to multiples of the fragment size,
95253a5a1b3Sopenharmony_ci                 * because OSS needs that (at least some versions
95353a5a1b3Sopenharmony_ci                 * do) */
95453a5a1b3Sopenharmony_ci                l = (l/(ssize_t) u->out_fragment_size) * (ssize_t) u->out_fragment_size;
95553a5a1b3Sopenharmony_ci
95653a5a1b3Sopenharmony_ci                /* Hmm, so poll() signalled us that we can read
95753a5a1b3Sopenharmony_ci                 * something, but GETOSPACE told us there was nothing?
95853a5a1b3Sopenharmony_ci                 * Hmm, make the best of it, try to read some data, to
95953a5a1b3Sopenharmony_ci                 * avoid spinning forever. */
96053a5a1b3Sopenharmony_ci                if (l <= 0 && (revents & POLLOUT)) {
96153a5a1b3Sopenharmony_ci                    l = (ssize_t) u->out_fragment_size;
96253a5a1b3Sopenharmony_ci                    loop = false;
96353a5a1b3Sopenharmony_ci                }
96453a5a1b3Sopenharmony_ci
96553a5a1b3Sopenharmony_ci                while (l > 0) {
96653a5a1b3Sopenharmony_ci                    void *p;
96753a5a1b3Sopenharmony_ci                    ssize_t t;
96853a5a1b3Sopenharmony_ci
96953a5a1b3Sopenharmony_ci                    if (u->memchunk.length <= 0)
97053a5a1b3Sopenharmony_ci                        pa_sink_render(u->sink, (size_t) l, &u->memchunk);
97153a5a1b3Sopenharmony_ci
97253a5a1b3Sopenharmony_ci                    pa_assert(u->memchunk.length > 0);
97353a5a1b3Sopenharmony_ci
97453a5a1b3Sopenharmony_ci                    p = pa_memblock_acquire(u->memchunk.memblock);
97553a5a1b3Sopenharmony_ci                    t = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
97653a5a1b3Sopenharmony_ci                    pa_memblock_release(u->memchunk.memblock);
97753a5a1b3Sopenharmony_ci
97853a5a1b3Sopenharmony_ci/*                     pa_log("wrote %i bytes of %u", t, l); */
97953a5a1b3Sopenharmony_ci
98053a5a1b3Sopenharmony_ci                    pa_assert(t != 0);
98153a5a1b3Sopenharmony_ci
98253a5a1b3Sopenharmony_ci                    if (t < 0) {
98353a5a1b3Sopenharmony_ci
98453a5a1b3Sopenharmony_ci                        if (errno == EAGAIN) {
98553a5a1b3Sopenharmony_ci                            pa_log_debug("EAGAIN");
98653a5a1b3Sopenharmony_ci
98753a5a1b3Sopenharmony_ci                            revents &= ~POLLOUT;
98853a5a1b3Sopenharmony_ci                            break;
98953a5a1b3Sopenharmony_ci
99053a5a1b3Sopenharmony_ci                        } else {
99153a5a1b3Sopenharmony_ci                            pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
99253a5a1b3Sopenharmony_ci                            goto fail;
99353a5a1b3Sopenharmony_ci                        }
99453a5a1b3Sopenharmony_ci
99553a5a1b3Sopenharmony_ci                    } else {
99653a5a1b3Sopenharmony_ci
99753a5a1b3Sopenharmony_ci                        u->memchunk.index += (size_t) t;
99853a5a1b3Sopenharmony_ci                        u->memchunk.length -= (size_t) t;
99953a5a1b3Sopenharmony_ci
100053a5a1b3Sopenharmony_ci                        if (u->memchunk.length <= 0) {
100153a5a1b3Sopenharmony_ci                            pa_memblock_unref(u->memchunk.memblock);
100253a5a1b3Sopenharmony_ci                            pa_memchunk_reset(&u->memchunk);
100353a5a1b3Sopenharmony_ci                        }
100453a5a1b3Sopenharmony_ci
100553a5a1b3Sopenharmony_ci                        l -= t;
100653a5a1b3Sopenharmony_ci
100753a5a1b3Sopenharmony_ci                        revents &= ~POLLOUT;
100853a5a1b3Sopenharmony_ci                        work_done = true;
100953a5a1b3Sopenharmony_ci                    }
101053a5a1b3Sopenharmony_ci
101153a5a1b3Sopenharmony_ci                    if (!loop)
101253a5a1b3Sopenharmony_ci                        break;
101353a5a1b3Sopenharmony_ci                }
101453a5a1b3Sopenharmony_ci
101553a5a1b3Sopenharmony_ci                if (work_done)
101653a5a1b3Sopenharmony_ci                    continue;
101753a5a1b3Sopenharmony_ci            }
101853a5a1b3Sopenharmony_ci        }
101953a5a1b3Sopenharmony_ci
102053a5a1b3Sopenharmony_ci        /* Try to read some data and pass it on to the source driver. */
102153a5a1b3Sopenharmony_ci
102253a5a1b3Sopenharmony_ci        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
102353a5a1b3Sopenharmony_ci
102453a5a1b3Sopenharmony_ci            if (u->use_mmap) {
102553a5a1b3Sopenharmony_ci
102653a5a1b3Sopenharmony_ci                if ((ret = mmap_read(u)) < 0)
102753a5a1b3Sopenharmony_ci                    goto fail;
102853a5a1b3Sopenharmony_ci
102953a5a1b3Sopenharmony_ci                revents &= ~POLLIN;
103053a5a1b3Sopenharmony_ci
103153a5a1b3Sopenharmony_ci                if (ret > 0)
103253a5a1b3Sopenharmony_ci                    continue;
103353a5a1b3Sopenharmony_ci
103453a5a1b3Sopenharmony_ci            } else {
103553a5a1b3Sopenharmony_ci
103653a5a1b3Sopenharmony_ci                void *p;
103753a5a1b3Sopenharmony_ci                ssize_t l;
103853a5a1b3Sopenharmony_ci                pa_memchunk memchunk;
103953a5a1b3Sopenharmony_ci                bool loop = false, work_done = false;
104053a5a1b3Sopenharmony_ci
104153a5a1b3Sopenharmony_ci                l = (ssize_t) u->in_fragment_size;
104253a5a1b3Sopenharmony_ci
104353a5a1b3Sopenharmony_ci                if (u->use_getispace) {
104453a5a1b3Sopenharmony_ci                    audio_buf_info info;
104553a5a1b3Sopenharmony_ci
104653a5a1b3Sopenharmony_ci                    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
104753a5a1b3Sopenharmony_ci                        pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
104853a5a1b3Sopenharmony_ci                        u->use_getispace = false;
104953a5a1b3Sopenharmony_ci                    } else {
105053a5a1b3Sopenharmony_ci                        l = info.bytes;
105153a5a1b3Sopenharmony_ci                        loop = true;
105253a5a1b3Sopenharmony_ci                    }
105353a5a1b3Sopenharmony_ci                }
105453a5a1b3Sopenharmony_ci
105553a5a1b3Sopenharmony_ci                l = (l/(ssize_t) u->in_fragment_size) * (ssize_t) u->in_fragment_size;
105653a5a1b3Sopenharmony_ci
105753a5a1b3Sopenharmony_ci                if (l <= 0 && (revents & POLLIN)) {
105853a5a1b3Sopenharmony_ci                    l = (ssize_t) u->in_fragment_size;
105953a5a1b3Sopenharmony_ci                    loop = false;
106053a5a1b3Sopenharmony_ci                }
106153a5a1b3Sopenharmony_ci
106253a5a1b3Sopenharmony_ci                while (l > 0) {
106353a5a1b3Sopenharmony_ci                    ssize_t t;
106453a5a1b3Sopenharmony_ci                    size_t k;
106553a5a1b3Sopenharmony_ci
106653a5a1b3Sopenharmony_ci                    pa_assert(l > 0);
106753a5a1b3Sopenharmony_ci
106853a5a1b3Sopenharmony_ci                    memchunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
106953a5a1b3Sopenharmony_ci
107053a5a1b3Sopenharmony_ci                    k = pa_memblock_get_length(memchunk.memblock);
107153a5a1b3Sopenharmony_ci
107253a5a1b3Sopenharmony_ci                    if (k > (size_t) l)
107353a5a1b3Sopenharmony_ci                        k = (size_t) l;
107453a5a1b3Sopenharmony_ci
107553a5a1b3Sopenharmony_ci                    k = (k/u->frame_size)*u->frame_size;
107653a5a1b3Sopenharmony_ci
107753a5a1b3Sopenharmony_ci                    p = pa_memblock_acquire(memchunk.memblock);
107853a5a1b3Sopenharmony_ci                    t = pa_read(u->fd, p, k, &read_type);
107953a5a1b3Sopenharmony_ci                    pa_memblock_release(memchunk.memblock);
108053a5a1b3Sopenharmony_ci
108153a5a1b3Sopenharmony_ci                    pa_assert(t != 0); /* EOF cannot happen */
108253a5a1b3Sopenharmony_ci
108353a5a1b3Sopenharmony_ci/*                     pa_log("read %i bytes of %u", t, l); */
108453a5a1b3Sopenharmony_ci
108553a5a1b3Sopenharmony_ci                    if (t < 0) {
108653a5a1b3Sopenharmony_ci                        pa_memblock_unref(memchunk.memblock);
108753a5a1b3Sopenharmony_ci
108853a5a1b3Sopenharmony_ci                        if (errno == EAGAIN) {
108953a5a1b3Sopenharmony_ci                            pa_log_debug("EAGAIN");
109053a5a1b3Sopenharmony_ci
109153a5a1b3Sopenharmony_ci                            revents &= ~POLLIN;
109253a5a1b3Sopenharmony_ci                            break;
109353a5a1b3Sopenharmony_ci
109453a5a1b3Sopenharmony_ci                        } else {
109553a5a1b3Sopenharmony_ci                            pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
109653a5a1b3Sopenharmony_ci                            goto fail;
109753a5a1b3Sopenharmony_ci                        }
109853a5a1b3Sopenharmony_ci
109953a5a1b3Sopenharmony_ci                    } else {
110053a5a1b3Sopenharmony_ci                        memchunk.index = 0;
110153a5a1b3Sopenharmony_ci                        memchunk.length = (size_t) t;
110253a5a1b3Sopenharmony_ci
110353a5a1b3Sopenharmony_ci                        pa_source_post(u->source, &memchunk);
110453a5a1b3Sopenharmony_ci                        pa_memblock_unref(memchunk.memblock);
110553a5a1b3Sopenharmony_ci
110653a5a1b3Sopenharmony_ci                        l -= t;
110753a5a1b3Sopenharmony_ci
110853a5a1b3Sopenharmony_ci                        revents &= ~POLLIN;
110953a5a1b3Sopenharmony_ci                        work_done = true;
111053a5a1b3Sopenharmony_ci                    }
111153a5a1b3Sopenharmony_ci
111253a5a1b3Sopenharmony_ci                    if (!loop)
111353a5a1b3Sopenharmony_ci                        break;
111453a5a1b3Sopenharmony_ci                }
111553a5a1b3Sopenharmony_ci
111653a5a1b3Sopenharmony_ci                if (work_done)
111753a5a1b3Sopenharmony_ci                    continue;
111853a5a1b3Sopenharmony_ci            }
111953a5a1b3Sopenharmony_ci        }
112053a5a1b3Sopenharmony_ci
112153a5a1b3Sopenharmony_ci/*         pa_log("loop2 revents=%i", revents); */
112253a5a1b3Sopenharmony_ci
112353a5a1b3Sopenharmony_ci        if (u->rtpoll_item) {
112453a5a1b3Sopenharmony_ci            struct pollfd *pollfd;
112553a5a1b3Sopenharmony_ci
112653a5a1b3Sopenharmony_ci            pa_assert(u->fd >= 0);
112753a5a1b3Sopenharmony_ci
112853a5a1b3Sopenharmony_ci            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
112953a5a1b3Sopenharmony_ci            pollfd->events = (short)
113053a5a1b3Sopenharmony_ci                (((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
113153a5a1b3Sopenharmony_ci                 ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0) |
113253a5a1b3Sopenharmony_ci                 POLLHUP);
113353a5a1b3Sopenharmony_ci        }
113453a5a1b3Sopenharmony_ci
113553a5a1b3Sopenharmony_ci        /* set a watchdog timeout of one second */
113653a5a1b3Sopenharmony_ci        pa_rtpoll_set_timer_relative(u->rtpoll, 1000000);
113753a5a1b3Sopenharmony_ci
113853a5a1b3Sopenharmony_ci        /* Hmm, nothing to do. Let's sleep */
113953a5a1b3Sopenharmony_ci        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) {
114053a5a1b3Sopenharmony_ci            goto fail;
114153a5a1b3Sopenharmony_ci        }
114253a5a1b3Sopenharmony_ci
114353a5a1b3Sopenharmony_ci        /* check for shutdown */
114453a5a1b3Sopenharmony_ci        if (u->shutdown) {
114553a5a1b3Sopenharmony_ci            goto fail;
114653a5a1b3Sopenharmony_ci        }
114753a5a1b3Sopenharmony_ci
114853a5a1b3Sopenharmony_ci        if (u->rtpoll_item) {
114953a5a1b3Sopenharmony_ci            struct pollfd *pollfd;
115053a5a1b3Sopenharmony_ci
115153a5a1b3Sopenharmony_ci            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
115253a5a1b3Sopenharmony_ci
115353a5a1b3Sopenharmony_ci            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
115453a5a1b3Sopenharmony_ci                pa_log("DSP shutdown.");
115553a5a1b3Sopenharmony_ci                goto fail;
115653a5a1b3Sopenharmony_ci            }
115753a5a1b3Sopenharmony_ci
115853a5a1b3Sopenharmony_ci            revents = pollfd->revents;
115953a5a1b3Sopenharmony_ci        } else
116053a5a1b3Sopenharmony_ci            revents = 0;
116153a5a1b3Sopenharmony_ci
116253a5a1b3Sopenharmony_ci        /* check for mixer shutdown, if any */
116353a5a1b3Sopenharmony_ci        if ((revents & (POLLOUT | POLLIN)) == 0) {
116453a5a1b3Sopenharmony_ci            int mixer_fd = u->mixer_fd;
116553a5a1b3Sopenharmony_ci            int devmask;
116653a5a1b3Sopenharmony_ci            if (mixer_fd > -1 && ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
116753a5a1b3Sopenharmony_ci                pa_log("Mixer shutdown.");
116853a5a1b3Sopenharmony_ci                goto fail;
116953a5a1b3Sopenharmony_ci            }
117053a5a1b3Sopenharmony_ci        }
117153a5a1b3Sopenharmony_ci    }
117253a5a1b3Sopenharmony_ci
117353a5a1b3Sopenharmony_cifail:
117453a5a1b3Sopenharmony_ci    /* If this was no regular exit from the loop we have to continue
117553a5a1b3Sopenharmony_ci     * processing messages until we received PA_MESSAGE_SHUTDOWN */
117653a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
117753a5a1b3Sopenharmony_ci    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
117853a5a1b3Sopenharmony_ci}
117953a5a1b3Sopenharmony_ci
118053a5a1b3Sopenharmony_ciint pa__init(pa_module*m) {
118153a5a1b3Sopenharmony_ci
118253a5a1b3Sopenharmony_ci    struct audio_buf_info info;
118353a5a1b3Sopenharmony_ci    struct userdata *u = NULL;
118453a5a1b3Sopenharmony_ci    const char *dev;
118553a5a1b3Sopenharmony_ci    int fd = -1;
118653a5a1b3Sopenharmony_ci    int nfrags, orig_frag_size, frag_size;
118753a5a1b3Sopenharmony_ci    int mode, caps;
118853a5a1b3Sopenharmony_ci    bool record = true, playback = true, use_mmap = true;
118953a5a1b3Sopenharmony_ci    pa_sample_spec ss;
119053a5a1b3Sopenharmony_ci    pa_channel_map map;
119153a5a1b3Sopenharmony_ci    pa_modargs *ma = NULL;
119253a5a1b3Sopenharmony_ci    char hwdesc[64];
119353a5a1b3Sopenharmony_ci    const char *name;
119453a5a1b3Sopenharmony_ci    bool namereg_fail;
119553a5a1b3Sopenharmony_ci    pa_sink_new_data sink_new_data;
119653a5a1b3Sopenharmony_ci    pa_source_new_data source_new_data;
119753a5a1b3Sopenharmony_ci
119853a5a1b3Sopenharmony_ci    pa_assert(m);
119953a5a1b3Sopenharmony_ci
120053a5a1b3Sopenharmony_ci    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
120153a5a1b3Sopenharmony_ci        pa_log("Failed to parse module arguments.");
120253a5a1b3Sopenharmony_ci        goto fail;
120353a5a1b3Sopenharmony_ci    }
120453a5a1b3Sopenharmony_ci
120553a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
120653a5a1b3Sopenharmony_ci        pa_log("record= and playback= expect boolean argument.");
120753a5a1b3Sopenharmony_ci        goto fail;
120853a5a1b3Sopenharmony_ci    }
120953a5a1b3Sopenharmony_ci
121053a5a1b3Sopenharmony_ci    if (!playback && !record) {
121153a5a1b3Sopenharmony_ci        pa_log("Neither playback nor record enabled for device.");
121253a5a1b3Sopenharmony_ci        goto fail;
121353a5a1b3Sopenharmony_ci    }
121453a5a1b3Sopenharmony_ci
121553a5a1b3Sopenharmony_ci    mode = (playback && record) ? O_RDWR : (playback ? O_WRONLY : O_RDONLY);
121653a5a1b3Sopenharmony_ci
121753a5a1b3Sopenharmony_ci    ss = m->core->default_sample_spec;
121853a5a1b3Sopenharmony_ci    map = m->core->default_channel_map;
121953a5a1b3Sopenharmony_ci    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_OSS) < 0) {
122053a5a1b3Sopenharmony_ci        pa_log("Failed to parse sample specification or channel map");
122153a5a1b3Sopenharmony_ci        goto fail;
122253a5a1b3Sopenharmony_ci    }
122353a5a1b3Sopenharmony_ci
122453a5a1b3Sopenharmony_ci    nfrags = (int) m->core->default_n_fragments;
122553a5a1b3Sopenharmony_ci    frag_size = (int) pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss);
122653a5a1b3Sopenharmony_ci    if (frag_size <= 0)
122753a5a1b3Sopenharmony_ci        frag_size = (int) pa_frame_size(&ss);
122853a5a1b3Sopenharmony_ci
122953a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
123053a5a1b3Sopenharmony_ci        pa_log("Failed to parse fragments arguments");
123153a5a1b3Sopenharmony_ci        goto fail;
123253a5a1b3Sopenharmony_ci    }
123353a5a1b3Sopenharmony_ci
123453a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
123553a5a1b3Sopenharmony_ci        pa_log("Failed to parse mmap argument.");
123653a5a1b3Sopenharmony_ci        goto fail;
123753a5a1b3Sopenharmony_ci    }
123853a5a1b3Sopenharmony_ci
123953a5a1b3Sopenharmony_ci    if ((fd = pa_oss_open(dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
124053a5a1b3Sopenharmony_ci        goto fail;
124153a5a1b3Sopenharmony_ci
124253a5a1b3Sopenharmony_ci    if (use_mmap && (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER))) {
124353a5a1b3Sopenharmony_ci        pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
124453a5a1b3Sopenharmony_ci        use_mmap = false;
124553a5a1b3Sopenharmony_ci    }
124653a5a1b3Sopenharmony_ci
124753a5a1b3Sopenharmony_ci#ifndef __FreeBSD__
124853a5a1b3Sopenharmony_ci    if (use_mmap && mode == O_WRONLY) {
124953a5a1b3Sopenharmony_ci        pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
125053a5a1b3Sopenharmony_ci        use_mmap = false;
125153a5a1b3Sopenharmony_ci    }
125253a5a1b3Sopenharmony_ci#endif
125353a5a1b3Sopenharmony_ci
125453a5a1b3Sopenharmony_ci    if (pa_oss_get_hw_description(dev, hwdesc, sizeof(hwdesc)) >= 0)
125553a5a1b3Sopenharmony_ci        pa_log_info("Hardware name is '%s'.", hwdesc);
125653a5a1b3Sopenharmony_ci    else
125753a5a1b3Sopenharmony_ci        hwdesc[0] = 0;
125853a5a1b3Sopenharmony_ci
125953a5a1b3Sopenharmony_ci    pa_log_info("Device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
126053a5a1b3Sopenharmony_ci
126153a5a1b3Sopenharmony_ci    orig_frag_size = frag_size;
126253a5a1b3Sopenharmony_ci    if (nfrags >= 2 && frag_size >= 1)
126353a5a1b3Sopenharmony_ci        if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
126453a5a1b3Sopenharmony_ci            goto fail;
126553a5a1b3Sopenharmony_ci
126653a5a1b3Sopenharmony_ci    if (pa_oss_auto_format(fd, &ss) < 0)
126753a5a1b3Sopenharmony_ci        goto fail;
126853a5a1b3Sopenharmony_ci
126953a5a1b3Sopenharmony_ci    if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
127053a5a1b3Sopenharmony_ci        pa_log("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno));
127153a5a1b3Sopenharmony_ci        goto fail;
127253a5a1b3Sopenharmony_ci    }
127353a5a1b3Sopenharmony_ci    pa_assert(frag_size > 0);
127453a5a1b3Sopenharmony_ci
127553a5a1b3Sopenharmony_ci    u = pa_xnew0(struct userdata, 1);
127653a5a1b3Sopenharmony_ci    u->core = m->core;
127753a5a1b3Sopenharmony_ci    u->module = m;
127853a5a1b3Sopenharmony_ci    m->userdata = u;
127953a5a1b3Sopenharmony_ci    u->fd = fd;
128053a5a1b3Sopenharmony_ci    u->mixer_fd = -1;
128153a5a1b3Sopenharmony_ci    u->mixer_devmask = 0;
128253a5a1b3Sopenharmony_ci    u->use_getospace = u->use_getispace = true;
128353a5a1b3Sopenharmony_ci    u->use_getodelay = true;
128453a5a1b3Sopenharmony_ci    u->mode = mode;
128553a5a1b3Sopenharmony_ci    u->frame_size = pa_frame_size(&ss);
128653a5a1b3Sopenharmony_ci    u->device_name = pa_xstrdup(dev);
128753a5a1b3Sopenharmony_ci    u->in_nfrags = u->out_nfrags = (uint32_t) (u->nfrags = nfrags);
128853a5a1b3Sopenharmony_ci    u->out_fragment_size = u->in_fragment_size = (uint32_t) (u->frag_size = frag_size);
128953a5a1b3Sopenharmony_ci    u->orig_frag_size = orig_frag_size;
129053a5a1b3Sopenharmony_ci    u->use_mmap = use_mmap;
129153a5a1b3Sopenharmony_ci    u->rtpoll = pa_rtpoll_new();
129253a5a1b3Sopenharmony_ci
129353a5a1b3Sopenharmony_ci    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
129453a5a1b3Sopenharmony_ci        pa_log("pa_thread_mq_init() failed.");
129553a5a1b3Sopenharmony_ci        goto fail;
129653a5a1b3Sopenharmony_ci    }
129753a5a1b3Sopenharmony_ci
129853a5a1b3Sopenharmony_ci    u->rtpoll_item = NULL;
129953a5a1b3Sopenharmony_ci    build_pollfd(u);
130053a5a1b3Sopenharmony_ci
130153a5a1b3Sopenharmony_ci    if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
130253a5a1b3Sopenharmony_ci        pa_log_info("Input -- %u fragments of size %u.", info.fragstotal, info.fragsize);
130353a5a1b3Sopenharmony_ci        u->in_fragment_size = (uint32_t) info.fragsize;
130453a5a1b3Sopenharmony_ci        u->in_nfrags = (uint32_t) info.fragstotal;
130553a5a1b3Sopenharmony_ci        u->use_getispace = true;
130653a5a1b3Sopenharmony_ci    }
130753a5a1b3Sopenharmony_ci
130853a5a1b3Sopenharmony_ci    if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
130953a5a1b3Sopenharmony_ci        pa_log_info("Output -- %u fragments of size %u.", info.fragstotal, info.fragsize);
131053a5a1b3Sopenharmony_ci        u->out_fragment_size = (uint32_t) info.fragsize;
131153a5a1b3Sopenharmony_ci        u->out_nfrags = (uint32_t) info.fragstotal;
131253a5a1b3Sopenharmony_ci        u->use_getospace = true;
131353a5a1b3Sopenharmony_ci    }
131453a5a1b3Sopenharmony_ci
131553a5a1b3Sopenharmony_ci    u->in_hwbuf_size = u->in_nfrags * u->in_fragment_size;
131653a5a1b3Sopenharmony_ci    u->out_hwbuf_size = u->out_nfrags * u->out_fragment_size;
131753a5a1b3Sopenharmony_ci
131853a5a1b3Sopenharmony_ci    if (mode != O_WRONLY) {
131953a5a1b3Sopenharmony_ci        char *name_buf = NULL;
132053a5a1b3Sopenharmony_ci
132153a5a1b3Sopenharmony_ci        if (use_mmap) {
132253a5a1b3Sopenharmony_ci            if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
132353a5a1b3Sopenharmony_ci                pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
132453a5a1b3Sopenharmony_ci                use_mmap = u->use_mmap = false;
132553a5a1b3Sopenharmony_ci                u->in_mmap = NULL;
132653a5a1b3Sopenharmony_ci            } else
132753a5a1b3Sopenharmony_ci                pa_log_debug("Successfully mmap()ed input buffer.");
132853a5a1b3Sopenharmony_ci        }
132953a5a1b3Sopenharmony_ci
133053a5a1b3Sopenharmony_ci        if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
133153a5a1b3Sopenharmony_ci            namereg_fail = true;
133253a5a1b3Sopenharmony_ci        else {
133353a5a1b3Sopenharmony_ci            name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev));
133453a5a1b3Sopenharmony_ci            namereg_fail = false;
133553a5a1b3Sopenharmony_ci        }
133653a5a1b3Sopenharmony_ci
133753a5a1b3Sopenharmony_ci        pa_source_new_data_init(&source_new_data);
133853a5a1b3Sopenharmony_ci        source_new_data.driver = __FILE__;
133953a5a1b3Sopenharmony_ci        source_new_data.module = m;
134053a5a1b3Sopenharmony_ci        pa_source_new_data_set_name(&source_new_data, name);
134153a5a1b3Sopenharmony_ci        source_new_data.namereg_fail = namereg_fail;
134253a5a1b3Sopenharmony_ci        pa_source_new_data_set_sample_spec(&source_new_data, &ss);
134353a5a1b3Sopenharmony_ci        pa_source_new_data_set_channel_map(&source_new_data, &map);
134453a5a1b3Sopenharmony_ci        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
134553a5a1b3Sopenharmony_ci        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "oss");
134653a5a1b3Sopenharmony_ci        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
134753a5a1b3Sopenharmony_ci        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
134853a5a1b3Sopenharmony_ci        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->in_hwbuf_size));
134953a5a1b3Sopenharmony_ci        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->in_fragment_size));
135053a5a1b3Sopenharmony_ci
135153a5a1b3Sopenharmony_ci        if (pa_modargs_get_proplist(ma, "source_properties", source_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
135253a5a1b3Sopenharmony_ci            pa_log("Invalid properties");
135353a5a1b3Sopenharmony_ci            pa_source_new_data_done(&source_new_data);
135453a5a1b3Sopenharmony_ci            pa_xfree(name_buf);
135553a5a1b3Sopenharmony_ci            goto fail;
135653a5a1b3Sopenharmony_ci        }
135753a5a1b3Sopenharmony_ci
135853a5a1b3Sopenharmony_ci        u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
135953a5a1b3Sopenharmony_ci        pa_source_new_data_done(&source_new_data);
136053a5a1b3Sopenharmony_ci        pa_xfree(name_buf);
136153a5a1b3Sopenharmony_ci
136253a5a1b3Sopenharmony_ci        if (!u->source) {
136353a5a1b3Sopenharmony_ci            pa_log("Failed to create source object");
136453a5a1b3Sopenharmony_ci            goto fail;
136553a5a1b3Sopenharmony_ci        }
136653a5a1b3Sopenharmony_ci
136753a5a1b3Sopenharmony_ci        u->source->parent.process_msg = source_process_msg;
136853a5a1b3Sopenharmony_ci        u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
136953a5a1b3Sopenharmony_ci        u->source->userdata = u;
137053a5a1b3Sopenharmony_ci
137153a5a1b3Sopenharmony_ci        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
137253a5a1b3Sopenharmony_ci        pa_source_set_rtpoll(u->source, u->rtpoll);
137353a5a1b3Sopenharmony_ci        pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->in_hwbuf_size, &u->source->sample_spec));
137453a5a1b3Sopenharmony_ci        u->source->refresh_volume = true;
137553a5a1b3Sopenharmony_ci
137653a5a1b3Sopenharmony_ci        if (use_mmap)
137753a5a1b3Sopenharmony_ci            u->in_mmap_memblocks = pa_xnew0(pa_memblock*, u->in_nfrags);
137853a5a1b3Sopenharmony_ci    }
137953a5a1b3Sopenharmony_ci
138053a5a1b3Sopenharmony_ci    if (mode != O_RDONLY) {
138153a5a1b3Sopenharmony_ci        char *name_buf = NULL;
138253a5a1b3Sopenharmony_ci
138353a5a1b3Sopenharmony_ci        if (use_mmap) {
138453a5a1b3Sopenharmony_ci            if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
138553a5a1b3Sopenharmony_ci                if (mode == O_RDWR) {
138653a5a1b3Sopenharmony_ci                    pa_log_debug("mmap() failed for input. Changing to O_WRONLY mode.");
138753a5a1b3Sopenharmony_ci                    mode = O_WRONLY;
138853a5a1b3Sopenharmony_ci                    goto go_on;
138953a5a1b3Sopenharmony_ci                } else {
139053a5a1b3Sopenharmony_ci                    pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
139153a5a1b3Sopenharmony_ci                    u->use_mmap = use_mmap = false;
139253a5a1b3Sopenharmony_ci                    u->out_mmap = NULL;
139353a5a1b3Sopenharmony_ci                }
139453a5a1b3Sopenharmony_ci            } else {
139553a5a1b3Sopenharmony_ci                pa_log_debug("Successfully mmap()ed output buffer.");
139653a5a1b3Sopenharmony_ci                pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss);
139753a5a1b3Sopenharmony_ci            }
139853a5a1b3Sopenharmony_ci        }
139953a5a1b3Sopenharmony_ci
140053a5a1b3Sopenharmony_ci        if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
140153a5a1b3Sopenharmony_ci            namereg_fail = true;
140253a5a1b3Sopenharmony_ci        else {
140353a5a1b3Sopenharmony_ci            name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev));
140453a5a1b3Sopenharmony_ci            namereg_fail = false;
140553a5a1b3Sopenharmony_ci        }
140653a5a1b3Sopenharmony_ci
140753a5a1b3Sopenharmony_ci        pa_sink_new_data_init(&sink_new_data);
140853a5a1b3Sopenharmony_ci        sink_new_data.driver = __FILE__;
140953a5a1b3Sopenharmony_ci        sink_new_data.module = m;
141053a5a1b3Sopenharmony_ci        pa_sink_new_data_set_name(&sink_new_data, name);
141153a5a1b3Sopenharmony_ci        sink_new_data.namereg_fail = namereg_fail;
141253a5a1b3Sopenharmony_ci        pa_sink_new_data_set_sample_spec(&sink_new_data, &ss);
141353a5a1b3Sopenharmony_ci        pa_sink_new_data_set_channel_map(&sink_new_data, &map);
141453a5a1b3Sopenharmony_ci        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
141553a5a1b3Sopenharmony_ci        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "oss");
141653a5a1b3Sopenharmony_ci        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
141753a5a1b3Sopenharmony_ci        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
141853a5a1b3Sopenharmony_ci        pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->out_hwbuf_size));
141953a5a1b3Sopenharmony_ci        pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->out_fragment_size));
142053a5a1b3Sopenharmony_ci
142153a5a1b3Sopenharmony_ci        if (pa_modargs_get_proplist(ma, "sink_properties", sink_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
142253a5a1b3Sopenharmony_ci            pa_log("Invalid properties");
142353a5a1b3Sopenharmony_ci            pa_sink_new_data_done(&sink_new_data);
142453a5a1b3Sopenharmony_ci            pa_xfree(name_buf);
142553a5a1b3Sopenharmony_ci            goto fail;
142653a5a1b3Sopenharmony_ci        }
142753a5a1b3Sopenharmony_ci
142853a5a1b3Sopenharmony_ci        u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
142953a5a1b3Sopenharmony_ci        pa_sink_new_data_done(&sink_new_data);
143053a5a1b3Sopenharmony_ci        pa_xfree(name_buf);
143153a5a1b3Sopenharmony_ci
143253a5a1b3Sopenharmony_ci        if (!u->sink) {
143353a5a1b3Sopenharmony_ci            pa_log("Failed to create sink object");
143453a5a1b3Sopenharmony_ci            goto fail;
143553a5a1b3Sopenharmony_ci        }
143653a5a1b3Sopenharmony_ci
143753a5a1b3Sopenharmony_ci        u->sink->parent.process_msg = sink_process_msg;
143853a5a1b3Sopenharmony_ci        u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
143953a5a1b3Sopenharmony_ci        u->sink->userdata = u;
144053a5a1b3Sopenharmony_ci
144153a5a1b3Sopenharmony_ci        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
144253a5a1b3Sopenharmony_ci        pa_sink_set_rtpoll(u->sink, u->rtpoll);
144353a5a1b3Sopenharmony_ci        pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->out_hwbuf_size, &u->sink->sample_spec));
144453a5a1b3Sopenharmony_ci        u->sink->refresh_volume = true;
144553a5a1b3Sopenharmony_ci
144653a5a1b3Sopenharmony_ci        pa_sink_set_max_request(u->sink, u->out_hwbuf_size);
144753a5a1b3Sopenharmony_ci
144853a5a1b3Sopenharmony_ci        if (use_mmap)
144953a5a1b3Sopenharmony_ci            u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags);
145053a5a1b3Sopenharmony_ci    }
145153a5a1b3Sopenharmony_ci
145253a5a1b3Sopenharmony_ci    if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
145353a5a1b3Sopenharmony_ci        bool do_close = true;
145453a5a1b3Sopenharmony_ci
145553a5a1b3Sopenharmony_ci        if (ioctl(u->mixer_fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
145653a5a1b3Sopenharmony_ci            pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno));
145753a5a1b3Sopenharmony_ci        else {
145853a5a1b3Sopenharmony_ci            if (u->sink && (u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM))) {
145953a5a1b3Sopenharmony_ci                pa_log_debug("Found hardware mixer track for playback.");
146053a5a1b3Sopenharmony_ci                pa_sink_set_get_volume_callback(u->sink, sink_get_volume);
146153a5a1b3Sopenharmony_ci                pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
146253a5a1b3Sopenharmony_ci                u->sink->n_volume_steps = 101;
146353a5a1b3Sopenharmony_ci                do_close = false;
146453a5a1b3Sopenharmony_ci            }
146553a5a1b3Sopenharmony_ci
146653a5a1b3Sopenharmony_ci            if (u->source && (u->mixer_devmask & (SOUND_MASK_MIC|SOUND_MASK_RECLEV|SOUND_MASK_IGAIN))) {
146753a5a1b3Sopenharmony_ci                pa_log_debug("Found hardware mixer track for recording.");
146853a5a1b3Sopenharmony_ci                pa_source_set_get_volume_callback(u->source, source_get_volume);
146953a5a1b3Sopenharmony_ci                pa_source_set_set_volume_callback(u->source, source_set_volume);
147053a5a1b3Sopenharmony_ci                u->source->n_volume_steps = 101;
147153a5a1b3Sopenharmony_ci                do_close = false;
147253a5a1b3Sopenharmony_ci            }
147353a5a1b3Sopenharmony_ci        }
147453a5a1b3Sopenharmony_ci
147553a5a1b3Sopenharmony_ci        if (do_close) {
147653a5a1b3Sopenharmony_ci            pa_close(u->mixer_fd);
147753a5a1b3Sopenharmony_ci            u->mixer_fd = -1;
147853a5a1b3Sopenharmony_ci            u->mixer_devmask = 0;
147953a5a1b3Sopenharmony_ci        }
148053a5a1b3Sopenharmony_ci    }
148153a5a1b3Sopenharmony_ci
148253a5a1b3Sopenharmony_cigo_on:
148353a5a1b3Sopenharmony_ci
148453a5a1b3Sopenharmony_ci    pa_assert(u->source || u->sink);
148553a5a1b3Sopenharmony_ci
148653a5a1b3Sopenharmony_ci    pa_memchunk_reset(&u->memchunk);
148753a5a1b3Sopenharmony_ci
148853a5a1b3Sopenharmony_ci    if (!(u->thread = pa_thread_new("oss", thread_func, u))) {
148953a5a1b3Sopenharmony_ci        pa_log("Failed to create thread.");
149053a5a1b3Sopenharmony_ci        goto fail;
149153a5a1b3Sopenharmony_ci    }
149253a5a1b3Sopenharmony_ci
149353a5a1b3Sopenharmony_ci    /* Read mixer settings */
149453a5a1b3Sopenharmony_ci    if (u->sink) {
149553a5a1b3Sopenharmony_ci        if (sink_new_data.volume_is_set) {
149653a5a1b3Sopenharmony_ci            if (u->sink->set_volume)
149753a5a1b3Sopenharmony_ci                u->sink->set_volume(u->sink);
149853a5a1b3Sopenharmony_ci        } else {
149953a5a1b3Sopenharmony_ci            if (u->sink->get_volume)
150053a5a1b3Sopenharmony_ci                u->sink->get_volume(u->sink);
150153a5a1b3Sopenharmony_ci        }
150253a5a1b3Sopenharmony_ci    }
150353a5a1b3Sopenharmony_ci
150453a5a1b3Sopenharmony_ci    if (u->source) {
150553a5a1b3Sopenharmony_ci        if (source_new_data.volume_is_set) {
150653a5a1b3Sopenharmony_ci            if (u->source->set_volume)
150753a5a1b3Sopenharmony_ci                u->source->set_volume(u->source);
150853a5a1b3Sopenharmony_ci        } else {
150953a5a1b3Sopenharmony_ci            if (u->source->get_volume)
151053a5a1b3Sopenharmony_ci                u->source->get_volume(u->source);
151153a5a1b3Sopenharmony_ci        }
151253a5a1b3Sopenharmony_ci    }
151353a5a1b3Sopenharmony_ci
151453a5a1b3Sopenharmony_ci    if (u->sink)
151553a5a1b3Sopenharmony_ci        pa_sink_put(u->sink);
151653a5a1b3Sopenharmony_ci    if (u->source)
151753a5a1b3Sopenharmony_ci        pa_source_put(u->source);
151853a5a1b3Sopenharmony_ci
151953a5a1b3Sopenharmony_ci    pa_modargs_free(ma);
152053a5a1b3Sopenharmony_ci
152153a5a1b3Sopenharmony_ci    return 0;
152253a5a1b3Sopenharmony_ci
152353a5a1b3Sopenharmony_cifail:
152453a5a1b3Sopenharmony_ci
152553a5a1b3Sopenharmony_ci    if (u)
152653a5a1b3Sopenharmony_ci        pa__done(m);
152753a5a1b3Sopenharmony_ci    else if (fd >= 0)
152853a5a1b3Sopenharmony_ci        pa_close(fd);
152953a5a1b3Sopenharmony_ci
153053a5a1b3Sopenharmony_ci    if (ma)
153153a5a1b3Sopenharmony_ci        pa_modargs_free(ma);
153253a5a1b3Sopenharmony_ci
153353a5a1b3Sopenharmony_ci    return -1;
153453a5a1b3Sopenharmony_ci}
153553a5a1b3Sopenharmony_ci
153653a5a1b3Sopenharmony_civoid pa__done(pa_module*m) {
153753a5a1b3Sopenharmony_ci    struct userdata *u;
153853a5a1b3Sopenharmony_ci
153953a5a1b3Sopenharmony_ci    pa_assert(m);
154053a5a1b3Sopenharmony_ci
154153a5a1b3Sopenharmony_ci    if (!(u = m->userdata))
154253a5a1b3Sopenharmony_ci        return;
154353a5a1b3Sopenharmony_ci
154453a5a1b3Sopenharmony_ci    if (u->sink)
154553a5a1b3Sopenharmony_ci        pa_sink_unlink(u->sink);
154653a5a1b3Sopenharmony_ci
154753a5a1b3Sopenharmony_ci    if (u->source)
154853a5a1b3Sopenharmony_ci        pa_source_unlink(u->source);
154953a5a1b3Sopenharmony_ci
155053a5a1b3Sopenharmony_ci    if (u->thread) {
155153a5a1b3Sopenharmony_ci        u->shutdown = true;
155253a5a1b3Sopenharmony_ci        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
155353a5a1b3Sopenharmony_ci        pa_thread_free(u->thread);
155453a5a1b3Sopenharmony_ci    }
155553a5a1b3Sopenharmony_ci
155653a5a1b3Sopenharmony_ci    pa_thread_mq_done(&u->thread_mq);
155753a5a1b3Sopenharmony_ci
155853a5a1b3Sopenharmony_ci    if (u->sink)
155953a5a1b3Sopenharmony_ci        pa_sink_unref(u->sink);
156053a5a1b3Sopenharmony_ci
156153a5a1b3Sopenharmony_ci    if (u->source)
156253a5a1b3Sopenharmony_ci        pa_source_unref(u->source);
156353a5a1b3Sopenharmony_ci
156453a5a1b3Sopenharmony_ci    if (u->memchunk.memblock)
156553a5a1b3Sopenharmony_ci        pa_memblock_unref(u->memchunk.memblock);
156653a5a1b3Sopenharmony_ci
156753a5a1b3Sopenharmony_ci    if (u->rtpoll_item)
156853a5a1b3Sopenharmony_ci        pa_rtpoll_item_free(u->rtpoll_item);
156953a5a1b3Sopenharmony_ci
157053a5a1b3Sopenharmony_ci    if (u->rtpoll)
157153a5a1b3Sopenharmony_ci        pa_rtpoll_free(u->rtpoll);
157253a5a1b3Sopenharmony_ci
157353a5a1b3Sopenharmony_ci    if (u->out_mmap_memblocks) {
157453a5a1b3Sopenharmony_ci        unsigned i;
157553a5a1b3Sopenharmony_ci        for (i = 0; i < u->out_nfrags; i++)
157653a5a1b3Sopenharmony_ci            if (u->out_mmap_memblocks[i])
157753a5a1b3Sopenharmony_ci                pa_memblock_unref_fixed(u->out_mmap_memblocks[i]);
157853a5a1b3Sopenharmony_ci        pa_xfree(u->out_mmap_memblocks);
157953a5a1b3Sopenharmony_ci    }
158053a5a1b3Sopenharmony_ci
158153a5a1b3Sopenharmony_ci    if (u->in_mmap_memblocks) {
158253a5a1b3Sopenharmony_ci        unsigned i;
158353a5a1b3Sopenharmony_ci        for (i = 0; i < u->in_nfrags; i++)
158453a5a1b3Sopenharmony_ci            if (u->in_mmap_memblocks[i])
158553a5a1b3Sopenharmony_ci                pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
158653a5a1b3Sopenharmony_ci        pa_xfree(u->in_mmap_memblocks);
158753a5a1b3Sopenharmony_ci    }
158853a5a1b3Sopenharmony_ci
158953a5a1b3Sopenharmony_ci    if (u->in_mmap && u->in_mmap != MAP_FAILED)
159053a5a1b3Sopenharmony_ci        munmap(u->in_mmap, u->in_hwbuf_size);
159153a5a1b3Sopenharmony_ci
159253a5a1b3Sopenharmony_ci    if (u->out_mmap && u->out_mmap != MAP_FAILED)
159353a5a1b3Sopenharmony_ci        munmap(u->out_mmap, u->out_hwbuf_size);
159453a5a1b3Sopenharmony_ci
159553a5a1b3Sopenharmony_ci    if (u->fd >= 0)
159653a5a1b3Sopenharmony_ci        pa_close(u->fd);
159753a5a1b3Sopenharmony_ci
159853a5a1b3Sopenharmony_ci    if (u->mixer_fd >= 0)
159953a5a1b3Sopenharmony_ci        pa_close(u->mixer_fd);
160053a5a1b3Sopenharmony_ci
160153a5a1b3Sopenharmony_ci    pa_xfree(u->device_name);
160253a5a1b3Sopenharmony_ci
160353a5a1b3Sopenharmony_ci    pa_xfree(u);
160453a5a1b3Sopenharmony_ci}
1605