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