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