From 22ab7da256ceea13963f9383e065d2ef8bfb8f5d Mon Sep 17 00:00:00 2001 From: Taylor Holberton Date: Sat, 1 Oct 2016 13:08:08 -0400 Subject: put source files into src dir --- pcm.c | 1097 ----------------------------------------------------------------- 1 file changed, 1097 deletions(-) delete mode 100644 pcm.c (limited to 'pcm.c') diff --git a/pcm.c b/pcm.c deleted file mode 100644 index 24ab333..0000000 --- a/pcm.c +++ /dev/null @@ -1,1097 +0,0 @@ -/* pcm.c -** -** Copyright 2011, The Android Open Source Project -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of The Android Open Source Project nor the names of -** its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND -** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE -** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -** DAMAGE. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#define __force -#define __bitwise -#define __user -#include - -#include - -#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL -#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2) - -static inline int param_is_mask(int p) -{ - return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && - (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); -} - -static inline int param_is_interval(int p) -{ - return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) && - (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL); -} - -static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n) -{ - return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); -} - -static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) -{ - return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); -} - -static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) -{ - if (bit >= SNDRV_MASK_MAX) - return; - if (param_is_mask(n)) { - struct snd_mask *m = param_to_mask(p, n); - m->bits[0] = 0; - m->bits[1] = 0; - m->bits[bit >> 5] |= (1 << (bit & 31)); - } -} - -static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val) -{ - if (param_is_interval(n)) { - struct snd_interval *i = param_to_interval(p, n); - i->min = val; - } -} - -static unsigned int param_get_min(struct snd_pcm_hw_params *p, int n) -{ - if (param_is_interval(n)) { - struct snd_interval *i = param_to_interval(p, n); - return i->min; - } - return 0; -} - -static unsigned int param_get_max(struct snd_pcm_hw_params *p, int n) -{ - if (param_is_interval(n)) { - struct snd_interval *i = param_to_interval(p, n); - return i->max; - } - return 0; -} - -static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val) -{ - if (param_is_interval(n)) { - struct snd_interval *i = param_to_interval(p, n); - i->min = val; - i->max = val; - i->integer = 1; - } -} - -static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n) -{ - if (param_is_interval(n)) { - struct snd_interval *i = param_to_interval(p, n); - if (i->integer) - return i->max; - } - return 0; -} - -static void param_init(struct snd_pcm_hw_params *p) -{ - int n; - - memset(p, 0, sizeof(*p)); - for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; - n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { - struct snd_mask *m = param_to_mask(p, n); - m->bits[0] = ~0; - m->bits[1] = ~0; - } - for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; - n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { - struct snd_interval *i = param_to_interval(p, n); - i->min = 0; - i->max = ~0; - } - p->rmask = ~0U; - p->cmask = 0; - p->info = ~0U; -} - -#define PCM_ERROR_MAX 128 - -struct pcm { - int fd; - unsigned int flags; - int running:1; - int prepared:1; - int underruns; - unsigned int buffer_size; - unsigned int boundary; - char error[PCM_ERROR_MAX]; - struct pcm_config config; - struct snd_pcm_mmap_status *mmap_status; - struct snd_pcm_mmap_control *mmap_control; - struct snd_pcm_sync_ptr *sync_ptr; - void *mmap_buffer; - unsigned int noirq_frames_per_msec; - long pcm_delay; - unsigned int subdevice; -}; - -unsigned int pcm_get_buffer_size(struct pcm *pcm) -{ - return pcm->buffer_size; -} - -int pcm_get_file_descriptor(struct pcm *pcm) -{ - return pcm->fd; -} - -const char* pcm_get_error(struct pcm *pcm) -{ - return pcm->error; -} - -unsigned int pcm_get_subdevice(struct pcm *pcm) -{ - return pcm->subdevice; -} - -static int oops(struct pcm *pcm, int e, const char *fmt, ...) -{ - va_list ap; - int sz; - - va_start(ap, fmt); - vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap); - va_end(ap); - sz = strlen(pcm->error); - - if (errno) - snprintf(pcm->error + sz, PCM_ERROR_MAX - sz, - ": %s", strerror(e)); - return -1; -} - -static unsigned int pcm_format_to_alsa(enum pcm_format format) -{ - switch (format) { - - case PCM_FORMAT_S8: - return SNDRV_PCM_FORMAT_S8; - - default: - case PCM_FORMAT_S16_LE: - return SNDRV_PCM_FORMAT_S16_LE; - case PCM_FORMAT_S16_BE: - return SNDRV_PCM_FORMAT_S16_BE; - - case PCM_FORMAT_S24_LE: - return SNDRV_PCM_FORMAT_S24_LE; - case PCM_FORMAT_S24_BE: - return SNDRV_PCM_FORMAT_S24_BE; - - case PCM_FORMAT_S24_3LE: - return SNDRV_PCM_FORMAT_S24_3LE; - case PCM_FORMAT_S24_3BE: - return SNDRV_PCM_FORMAT_S24_3BE; - - case PCM_FORMAT_S32_LE: - return SNDRV_PCM_FORMAT_S32_LE; - case PCM_FORMAT_S32_BE: - return SNDRV_PCM_FORMAT_S32_BE; - }; -} - -unsigned int pcm_format_to_bits(enum pcm_format format) -{ - switch (format) { - case PCM_FORMAT_S32_LE: - case PCM_FORMAT_S32_BE: - case PCM_FORMAT_S24_LE: - case PCM_FORMAT_S24_BE: - return 32; - case PCM_FORMAT_S24_3LE: - case PCM_FORMAT_S24_3BE: - return 24; - default: - case PCM_FORMAT_S16_LE: - case PCM_FORMAT_S16_BE: - return 16; - case PCM_FORMAT_S8: - return 8; - }; -} - -unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes) -{ - return bytes / (pcm->config.channels * - (pcm_format_to_bits(pcm->config.format) >> 3)); -} - -unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames) -{ - return frames * pcm->config.channels * - (pcm_format_to_bits(pcm->config.format) >> 3); -} - -static int pcm_sync_ptr(struct pcm *pcm, int flags) -{ - if (pcm->sync_ptr) { - pcm->sync_ptr->flags = flags; - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0) - return -1; - } - return 0; -} - -static int pcm_hw_mmap_status(struct pcm *pcm) -{ - if (pcm->sync_ptr) - return 0; - - int page_size = sysconf(_SC_PAGE_SIZE); - pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED, - pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS); - if (pcm->mmap_status == MAP_FAILED) - pcm->mmap_status = NULL; - if (!pcm->mmap_status) - goto mmap_error; - - pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL); - if (pcm->mmap_control == MAP_FAILED) - pcm->mmap_control = NULL; - if (!pcm->mmap_control) { - munmap(pcm->mmap_status, page_size); - pcm->mmap_status = NULL; - goto mmap_error; - } - pcm->mmap_control->avail_min = 1; - - return 0; - -mmap_error: - - pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr)); - if (!pcm->sync_ptr) - return -ENOMEM; - pcm->mmap_status = &pcm->sync_ptr->s.status; - pcm->mmap_control = &pcm->sync_ptr->c.control; - pcm->mmap_control->avail_min = 1; - pcm_sync_ptr(pcm, 0); - - return 0; -} - -static void pcm_hw_munmap_status(struct pcm *pcm) { - if (pcm->sync_ptr) { - free(pcm->sync_ptr); - pcm->sync_ptr = NULL; - } else { - int page_size = sysconf(_SC_PAGE_SIZE); - if (pcm->mmap_status) - munmap(pcm->mmap_status, page_size); - if (pcm->mmap_control) - munmap(pcm->mmap_control, page_size); - } - pcm->mmap_status = NULL; - pcm->mmap_control = NULL; -} - -static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset, - char *buf, unsigned int src_offset, - unsigned int frames) -{ - int size_bytes = pcm_frames_to_bytes(pcm, frames); - int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset); - int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset); - - /* interleaved only atm */ - if (pcm->flags & PCM_IN) - memcpy(buf + src_offset_bytes, - (char*)pcm->mmap_buffer + pcm_offset_bytes, - size_bytes); - else - memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes, - buf + src_offset_bytes, - size_bytes); - return 0; -} - -static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf, - unsigned int offset, unsigned int size) -{ - void *pcm_areas; - int commit; - unsigned int pcm_offset, frames, count = 0; - - while (size > 0) { - frames = size; - pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames); - pcm_areas_copy(pcm, pcm_offset, buf, offset, frames); - commit = pcm_mmap_commit(pcm, pcm_offset, frames); - if (commit < 0) { - oops(pcm, commit, "failed to commit %d frames\n", frames); - return commit; - } - - offset += commit; - count += commit; - size -= commit; - } - return count; -} - -int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail, - struct timespec *tstamp) -{ - int frames; - int rc; - snd_pcm_uframes_t hw_ptr; - - if (!pcm_is_ready(pcm)) - return -1; - - rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC); - if (rc < 0) - return -1; - - if ((pcm->mmap_status->state != PCM_STATE_RUNNING) && - (pcm->mmap_status->state != PCM_STATE_DRAINING)) - return -1; - - *tstamp = pcm->mmap_status->tstamp; - if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0) - return -1; - - hw_ptr = pcm->mmap_status->hw_ptr; - if (pcm->flags & PCM_IN) - frames = hw_ptr - pcm->mmap_control->appl_ptr; - else - frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; - - if (frames < 0) - return -1; - - *avail = (unsigned int)frames; - - return 0; -} - -int pcm_write(struct pcm *pcm, const void *data, unsigned int count) -{ - struct snd_xferi x; - - if (pcm->flags & PCM_IN) - return -EINVAL; - - x.buf = (void*)data; - x.frames = count / (pcm->config.channels * - pcm_format_to_bits(pcm->config.format) / 8); - - for (;;) { - if (!pcm->running) { - int prepare_error = pcm_prepare(pcm); - if (prepare_error) - return prepare_error; - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) - return oops(pcm, errno, "cannot write initial data"); - pcm->running = 1; - return 0; - } - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { - pcm->prepared = 0; - pcm->running = 0; - if (errno == EPIPE) { - /* we failed to make our window -- try to restart if we are - * allowed to do so. Otherwise, simply allow the EPIPE error to - * propagate up to the app level */ - pcm->underruns++; - if (pcm->flags & PCM_NORESTART) - return -EPIPE; - continue; - } - return oops(pcm, errno, "cannot write stream data"); - } - return 0; - } -} - -int pcm_read(struct pcm *pcm, void *data, unsigned int count) -{ - struct snd_xferi x; - - if (!(pcm->flags & PCM_IN)) - return -EINVAL; - - x.buf = data; - x.frames = count / (pcm->config.channels * - pcm_format_to_bits(pcm->config.format) / 8); - - for (;;) { - if (!pcm->running) { - if (pcm_start(pcm) < 0) { - fprintf(stderr, "start error"); - return -errno; - } - } - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { - pcm->prepared = 0; - pcm->running = 0; - if (errno == EPIPE) { - /* we failed to make our window -- try to restart */ - pcm->underruns++; - continue; - } - return oops(pcm, errno, "cannot read stream data"); - } - return 0; - } -} - -static struct pcm bad_pcm = { - .fd = -1, -}; - -struct pcm_params *pcm_params_get(unsigned int card, unsigned int device, - unsigned int flags) -{ - struct snd_pcm_hw_params *params; - char fn[256]; - int fd; - - snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device, - flags & PCM_IN ? 'c' : 'p'); - - fd = open(fn, O_RDWR); - if (fd < 0) { - fprintf(stderr, "cannot open device '%s'\n", fn); - goto err_open; - } - - params = calloc(1, sizeof(struct snd_pcm_hw_params)); - if (!params) - goto err_calloc; - - param_init(params); - if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) { - fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno); - goto err_hw_refine; - } - - close(fd); - - return (struct pcm_params *)params; - -err_hw_refine: - free(params); -err_calloc: - close(fd); -err_open: - return NULL; -} - -void pcm_params_free(struct pcm_params *pcm_params) -{ - struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; - - if (params) - free(params); -} - -static int pcm_param_to_alsa(enum pcm_param param) -{ - switch (param) { - case PCM_PARAM_ACCESS: - return SNDRV_PCM_HW_PARAM_ACCESS; - case PCM_PARAM_FORMAT: - return SNDRV_PCM_HW_PARAM_FORMAT; - case PCM_PARAM_SUBFORMAT: - return SNDRV_PCM_HW_PARAM_SUBFORMAT; - case PCM_PARAM_SAMPLE_BITS: - return SNDRV_PCM_HW_PARAM_SAMPLE_BITS; - break; - case PCM_PARAM_FRAME_BITS: - return SNDRV_PCM_HW_PARAM_FRAME_BITS; - break; - case PCM_PARAM_CHANNELS: - return SNDRV_PCM_HW_PARAM_CHANNELS; - break; - case PCM_PARAM_RATE: - return SNDRV_PCM_HW_PARAM_RATE; - break; - case PCM_PARAM_PERIOD_TIME: - return SNDRV_PCM_HW_PARAM_PERIOD_TIME; - break; - case PCM_PARAM_PERIOD_SIZE: - return SNDRV_PCM_HW_PARAM_PERIOD_SIZE; - break; - case PCM_PARAM_PERIOD_BYTES: - return SNDRV_PCM_HW_PARAM_PERIOD_BYTES; - break; - case PCM_PARAM_PERIODS: - return SNDRV_PCM_HW_PARAM_PERIODS; - break; - case PCM_PARAM_BUFFER_TIME: - return SNDRV_PCM_HW_PARAM_BUFFER_TIME; - break; - case PCM_PARAM_BUFFER_SIZE: - return SNDRV_PCM_HW_PARAM_BUFFER_SIZE; - break; - case PCM_PARAM_BUFFER_BYTES: - return SNDRV_PCM_HW_PARAM_BUFFER_BYTES; - break; - case PCM_PARAM_TICK_TIME: - return SNDRV_PCM_HW_PARAM_TICK_TIME; - break; - - default: - return -1; - } -} - -struct pcm_mask *pcm_params_get_mask(struct pcm_params *pcm_params, - enum pcm_param param) -{ - int p; - struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; - if (params == NULL) { - return NULL; - } - - p = pcm_param_to_alsa(param); - if (p < 0 || !param_is_mask(p)) { - return NULL; - } - - return (struct pcm_mask *)param_to_mask(params, p); -} - -unsigned int pcm_params_get_min(struct pcm_params *pcm_params, - enum pcm_param param) -{ - struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; - int p; - - if (!params) - return 0; - - p = pcm_param_to_alsa(param); - if (p < 0) - return 0; - - return param_get_min(params, p); -} - -unsigned int pcm_params_get_max(struct pcm_params *pcm_params, - enum pcm_param param) -{ - struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; - int p; - - if (!params) - return 0; - - p = pcm_param_to_alsa(param); - if (p < 0) - return 0; - - return param_get_max(params, p); -} - -int pcm_close(struct pcm *pcm) -{ - if (pcm == &bad_pcm) - return 0; - - pcm_hw_munmap_status(pcm); - - if (pcm->flags & PCM_MMAP) { - pcm_stop(pcm); - munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size)); - } - - if (pcm->fd >= 0) - close(pcm->fd); - pcm->prepared = 0; - pcm->running = 0; - pcm->buffer_size = 0; - pcm->fd = -1; - free(pcm); - return 0; -} - -struct pcm *pcm_open(unsigned int card, unsigned int device, - unsigned int flags, struct pcm_config *config) -{ - struct pcm *pcm; - struct snd_pcm_info info; - struct snd_pcm_hw_params params; - struct snd_pcm_sw_params sparams; - char fn[256]; - int rc; - - pcm = calloc(1, sizeof(struct pcm)); - if (!pcm || !config) - return &bad_pcm; /* TODO: could support default config here */ - - pcm->config = *config; - - snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device, - flags & PCM_IN ? 'c' : 'p'); - - pcm->flags = flags; - pcm->fd = open(fn, O_RDWR); - if (pcm->fd < 0) { - oops(pcm, errno, "cannot open device '%s'", fn); - return pcm; - } - - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) { - oops(pcm, errno, "cannot get info"); - goto fail_close; - } - pcm->subdevice = info.subdevice; - - param_init(¶ms); - param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, - pcm_format_to_alsa(config->format)); - param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT, - SNDRV_PCM_SUBFORMAT_STD); - param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size); - param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - pcm_format_to_bits(config->format)); - param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, - pcm_format_to_bits(config->format) * config->channels); - param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, - config->channels); - param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count); - param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate); - - if (flags & PCM_NOIRQ) { - - if (!(flags & PCM_MMAP)) { - oops(pcm, -EINVAL, "noirq only currently supported with mmap()."); - goto fail; - } - - params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP; - pcm->noirq_frames_per_msec = config->rate / 1000; - } - - if (flags & PCM_MMAP) - param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); - else - param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_ACCESS_RW_INTERLEAVED); - - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { - oops(pcm, errno, "cannot set hw params"); - goto fail_close; - } - - /* get our refined hw_params */ - config->period_size = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); - config->period_count = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS); - pcm->buffer_size = config->period_count * config->period_size; - - if (flags & PCM_MMAP) { - pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size), - PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0); - if (pcm->mmap_buffer == MAP_FAILED) { - oops(pcm, -errno, "failed to mmap buffer %d bytes\n", - pcm_frames_to_bytes(pcm, pcm->buffer_size)); - goto fail_close; - } - } - - - memset(&sparams, 0, sizeof(sparams)); - sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE; - sparams.period_step = 1; - sparams.avail_min = 1; - - if (!config->start_threshold) { - if (pcm->flags & PCM_IN) - pcm->config.start_threshold = sparams.start_threshold = 1; - else - pcm->config.start_threshold = sparams.start_threshold = - config->period_count * config->period_size / 2; - } else - sparams.start_threshold = config->start_threshold; - - /* pick a high stop threshold - todo: does this need further tuning */ - if (!config->stop_threshold) { - if (pcm->flags & PCM_IN) - pcm->config.stop_threshold = sparams.stop_threshold = - config->period_count * config->period_size * 10; - else - pcm->config.stop_threshold = sparams.stop_threshold = - config->period_count * config->period_size; - } - else - sparams.stop_threshold = config->stop_threshold; - - sparams.xfer_align = config->period_size / 2; /* needed for old kernels */ - sparams.silence_size = 0; - sparams.silence_threshold = config->silence_threshold; - pcm->boundary = sparams.boundary = pcm->buffer_size; - - while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size) - pcm->boundary *= 2; - - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) { - oops(pcm, errno, "cannot set sw params"); - goto fail; - } - - rc = pcm_hw_mmap_status(pcm); - if (rc < 0) { - oops(pcm, rc, "mmap status failed"); - goto fail; - } - -#ifdef SNDRV_PCM_IOCTL_TTSTAMP - if (pcm->flags & PCM_MONOTONIC) { - int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; - rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg); - if (rc < 0) { - oops(pcm, rc, "cannot set timestamp type"); - goto fail; - } - } -#endif - - pcm->underruns = 0; - return pcm; - -fail: - if (flags & PCM_MMAP) - munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size)); -fail_close: - close(pcm->fd); - pcm->fd = -1; - return pcm; -} - -int pcm_is_ready(struct pcm *pcm) -{ - return pcm->fd >= 0; -} - -int pcm_prepare(struct pcm *pcm) -{ - if (pcm->prepared) - return 0; - - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0) - return oops(pcm, errno, "cannot prepare channel"); - - pcm->prepared = 1; - return 0; -} - -int pcm_start(struct pcm *pcm) -{ - int prepare_error = pcm_prepare(pcm); - if (prepare_error) - return prepare_error; - - if (pcm->flags & PCM_MMAP) - pcm_sync_ptr(pcm, 0); - - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0) - return oops(pcm, errno, "cannot start channel"); - - pcm->running = 1; - return 0; -} - -int pcm_stop(struct pcm *pcm) -{ - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0) - return oops(pcm, errno, "cannot stop channel"); - - pcm->prepared = 0; - pcm->running = 0; - return 0; -} - -static inline int pcm_mmap_playback_avail(struct pcm *pcm) -{ - int avail; - - avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; - - if (avail < 0) - avail += pcm->boundary; - else if (avail >= (int)pcm->boundary) - avail -= pcm->boundary; - - return avail; -} - -static inline int pcm_mmap_capture_avail(struct pcm *pcm) -{ - int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr; - if (avail < 0) - avail += pcm->boundary; - return avail; -} - -static inline int pcm_mmap_avail(struct pcm *pcm) -{ - pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC); - if (pcm->flags & PCM_IN) - return pcm_mmap_capture_avail(pcm); - else - return pcm_mmap_playback_avail(pcm); -} - -static void pcm_mmap_appl_forward(struct pcm *pcm, int frames) -{ - unsigned int appl_ptr = pcm->mmap_control->appl_ptr; - appl_ptr += frames; - - /* check for boundary wrap */ - if (appl_ptr > pcm->boundary) - appl_ptr -= pcm->boundary; - pcm->mmap_control->appl_ptr = appl_ptr; -} - -int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, - unsigned int *frames) -{ - unsigned int continuous, copy_frames, avail; - - /* return the mmap buffer */ - *areas = pcm->mmap_buffer; - - /* and the application offset in frames */ - *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size; - - avail = pcm_mmap_avail(pcm); - if (avail > pcm->buffer_size) - avail = pcm->buffer_size; - continuous = pcm->buffer_size - *offset; - - /* we can only copy frames if the are availabale and continuos */ - copy_frames = *frames; - if (copy_frames > avail) - copy_frames = avail; - if (copy_frames > continuous) - copy_frames = continuous; - *frames = copy_frames; - - return 0; -} - -int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames) -{ - /* not used */ - (void) offset; - - /* update the application pointer in userspace and kernel */ - pcm_mmap_appl_forward(pcm, frames); - pcm_sync_ptr(pcm, 0); - - return frames; -} - -int pcm_avail_update(struct pcm *pcm) -{ - pcm_sync_ptr(pcm, 0); - return pcm_mmap_avail(pcm); -} - -int pcm_state(struct pcm *pcm) -{ - int err = pcm_sync_ptr(pcm, 0); - if (err < 0) - return err; - - return pcm->mmap_status->state; -} - -int pcm_wait(struct pcm *pcm, int timeout) -{ - struct pollfd pfd; - int err; - - pfd.fd = pcm->fd; - pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL; - - do { - /* let's wait for avail or timeout */ - err = poll(&pfd, 1, timeout); - if (err < 0) - return -errno; - - /* timeout ? */ - if (err == 0) - return 0; - - /* have we been interrupted ? */ - if (errno == -EINTR) - continue; - - /* check for any errors */ - if (pfd.revents & (POLLERR | POLLNVAL)) { - switch (pcm_state(pcm)) { - case PCM_STATE_XRUN: - return -EPIPE; - case PCM_STATE_SUSPENDED: - return -ESTRPIPE; - case PCM_STATE_DISCONNECTED: - return -ENODEV; - default: - return -EIO; - } - } - /* poll again if fd not ready for IO */ - } while (!(pfd.revents & (POLLIN | POLLOUT))); - - return 1; -} - -int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes) -{ - int err = 0, frames, avail; - unsigned int offset = 0, count; - - if (bytes == 0) - return 0; - - count = pcm_bytes_to_frames(pcm, bytes); - - while (count > 0) { - - /* get the available space for writing new frames */ - avail = pcm_avail_update(pcm); - if (avail < 0) { - fprintf(stderr, "cannot determine available mmap frames"); - return err; - } - - /* start the audio if we reach the threshold */ - if (!pcm->running && - (pcm->buffer_size - avail) >= pcm->config.start_threshold) { - if (pcm_start(pcm) < 0) { - fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n", - (unsigned int)pcm->mmap_status->hw_ptr, - (unsigned int)pcm->mmap_control->appl_ptr, - avail); - return -errno; - } - } - - /* sleep until we have space to write new frames */ - if (pcm->running && - (unsigned int)avail < pcm->mmap_control->avail_min) { - int time = -1; - - if (pcm->flags & PCM_NOIRQ) - time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min) - / pcm->noirq_frames_per_msec; - - err = pcm_wait(pcm, time); - if (err < 0) { - pcm->prepared = 0; - pcm->running = 0; - fprintf(stderr, "wait error: hw 0x%x app 0x%x avail 0x%x\n", - (unsigned int)pcm->mmap_status->hw_ptr, - (unsigned int)pcm->mmap_control->appl_ptr, - avail); - pcm->mmap_control->appl_ptr = 0; - return err; - } - continue; - } - - frames = count; - if (frames > avail) - frames = avail; - - if (!frames) - break; - - /* copy frames from buffer */ - frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames); - if (frames < 0) { - fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n", - (unsigned int)pcm->mmap_status->hw_ptr, - (unsigned int)pcm->mmap_control->appl_ptr, - avail); - return frames; - } - - offset += frames; - count -= frames; - } - - return 0; -} - -int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count) -{ - if ((~pcm->flags) & (PCM_OUT | PCM_MMAP)) - return -ENOSYS; - - return pcm_mmap_transfer(pcm, (void *)data, count); -} - -int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count) -{ - if ((~pcm->flags) & (PCM_IN | PCM_MMAP)) - return -ENOSYS; - - return pcm_mmap_transfer(pcm, data, count); -} - -long pcm_get_delay(struct pcm *pcm) -{ - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DELAY, &pcm->pcm_delay) < 0) - return -1; - - return pcm->pcm_delay; -} -- cgit v1.2.3