pcm.c revision 1b32ddfd35e54b0a6ef1e43ca35dbc1547e5676a
1edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson/* pcm.c 2edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** 3edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** Copyright 2011, The Android Open Source Project 4edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** 5edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** Redistribution and use in source and binary forms, with or without 6edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** modification, are permitted provided that the following conditions are met: 7edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** * Redistributions of source code must retain the above copyright 8edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** notice, this list of conditions and the following disclaimer. 9edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** * Redistributions in binary form must reproduce the above copyright 10edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** notice, this list of conditions and the following disclaimer in the 11edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** documentation and/or other materials provided with the distribution. 12edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** * Neither the name of The Android Open Source Project nor the names of 13edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** its contributors may be used to endorse or promote products derived 14edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** from this software without specific prior written permission. 15edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** 16edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND 17edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE 20edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** DAMAGE. 27edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson*/ 28edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 29edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <stdio.h> 30edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <stdlib.h> 31edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <fcntl.h> 32edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <stdarg.h> 33edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <string.h> 34edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <errno.h> 35edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <unistd.h> 36e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson#include <poll.h> 37edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 38edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <sys/ioctl.h> 39edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <sys/mman.h> 40edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <sys/time.h> 41e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson#include <limits.h> 42edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 43edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <linux/ioctl.h> 44edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define __force 45edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define __bitwise 46edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define __user 47edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <sound/asound.h> 48edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 49edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <tinyalsa/asoundlib.h> 50edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 51edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL 52e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2) 53edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 54edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic inline int param_is_mask(int p) 55edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 56edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && 57edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); 58edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 59edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 60edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic inline int param_is_interval(int p) 61edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 62edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) && 63edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL); 64edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 65edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 66edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n) 67edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 68edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); 69edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 70edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 71edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) 72edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 73edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); 74edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 75edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 76edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) 77edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 78edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (bit >= SNDRV_MASK_MAX) 79edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return; 80edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (param_is_mask(n)) { 81edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_mask *m = param_to_mask(p, n); 82edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[0] = 0; 83edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[1] = 0; 84edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[bit >> 5] |= (1 << (bit & 31)); 85edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 86edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 87edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 88edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val) 89edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 90edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (param_is_interval(n)) { 91edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_interval *i = param_to_interval(p, n); 92edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->min = val; 93edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 94edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 95edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 96edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned int val) 97edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 98edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (param_is_interval(n)) { 99edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_interval *i = param_to_interval(p, n); 100edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->max = val; 101edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 102edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 103edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 104edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val) 105edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 106edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (param_is_interval(n)) { 107edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_interval *i = param_to_interval(p, n); 108edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->min = val; 109edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->max = val; 110edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->integer = 1; 111edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 112edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 113edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 114e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic unsigned int param_get_int(struct snd_pcm_hw_params *p, int n) 115e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 116e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (param_is_interval(n)) { 117e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson struct snd_interval *i = param_to_interval(p, n); 118e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (i->integer) 119e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return i->max; 120e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 121e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 122e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 123e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 124edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_init(struct snd_pcm_hw_params *p) 125edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 126edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int n; 127edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 128edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson memset(p, 0, sizeof(*p)); 129edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; 130edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { 131edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_mask *m = param_to_mask(p, n); 132edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[0] = ~0; 133edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[1] = ~0; 134edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 135edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; 136edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { 137edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_interval *i = param_to_interval(p, n); 138edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->min = 0; 139edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->max = ~0; 140edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 141edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 142edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 143edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define PCM_ERROR_MAX 128 144edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 145edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstruct pcm { 146edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int fd; 147edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson unsigned int flags; 148edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int running:1; 149edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int underruns; 150edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson unsigned int buffer_size; 151e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int boundary; 152edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson char error[PCM_ERROR_MAX]; 153edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct pcm_config config; 154dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson struct snd_pcm_mmap_status *mmap_status; 155dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson struct snd_pcm_mmap_control *mmap_control; 156dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson struct snd_pcm_sync_ptr *sync_ptr; 157e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson void *mmap_buffer; 158e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int noirq_frames_per_msec; 15973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent int wait_for_avail_min; 160edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson}; 161edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 162edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonunsigned int pcm_get_buffer_size(struct pcm *pcm) 163edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 164edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm->buffer_size; 165edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 166edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 167edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonconst char* pcm_get_error(struct pcm *pcm) 168edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 169edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm->error; 170edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 171edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 172edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic int oops(struct pcm *pcm, int e, const char *fmt, ...) 173edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 174edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson va_list ap; 175edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int sz; 176edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 177edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson va_start(ap, fmt); 178edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap); 179edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson va_end(ap); 180edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sz = strlen(pcm->error); 181edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 182edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (errno) 183edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson snprintf(pcm->error + sz, PCM_ERROR_MAX - sz, 184edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson ": %s", strerror(e)); 185edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return -1; 186edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 187edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 18883b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilsonstatic unsigned int pcm_format_to_alsa(enum pcm_format format) 18983b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson{ 19083b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson switch (format) { 19183b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson case PCM_FORMAT_S32_LE: 19283b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson return SNDRV_PCM_FORMAT_S32_LE; 19383b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson default: 19483b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson case PCM_FORMAT_S16_LE: 19583b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson return SNDRV_PCM_FORMAT_S16_LE; 19683b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson }; 19783b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson} 19883b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson 19983b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilsonstatic unsigned int pcm_format_to_bits(enum pcm_format format) 20083b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson{ 20183b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson switch (format) { 20283b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson case PCM_FORMAT_S32_LE: 20383b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson return 32; 20483b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson default: 20583b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson case PCM_FORMAT_S16_LE: 20683b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson return 16; 20783b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson }; 20883b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson} 20983b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson 210e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonunsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes) 211e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 212e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return bytes / (pcm->config.channels * 213e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (pcm_format_to_bits(pcm->config.format) >> 3)); 214e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 215e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 216e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonunsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames) 217e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 218e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return frames * pcm->config.channels * 219e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (pcm_format_to_bits(pcm->config.format) >> 3); 220e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 221e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 222dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilsonstatic int pcm_sync_ptr(struct pcm *pcm, int flags) { 223dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->sync_ptr) { 224dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->sync_ptr->flags = flags; 225dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0) 226dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return -1; 227dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson } 228dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return 0; 229dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson} 230dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 231dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilsonstatic int pcm_hw_mmap_status(struct pcm *pcm) { 232dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 233dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->sync_ptr) 234dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return 0; 235dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 236dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson int page_size = sysconf(_SC_PAGE_SIZE); 237dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED, 238dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS); 239dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->mmap_status == MAP_FAILED) 240dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_status = NULL; 241dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (!pcm->mmap_status) 242dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson goto mmap_error; 243dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 244dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE, 245dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL); 246dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->mmap_control == MAP_FAILED) 247dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_control = NULL; 248dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (!pcm->mmap_control) { 249dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson munmap(pcm->mmap_status, page_size); 250dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_status = NULL; 251dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson goto mmap_error; 252dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson } 25373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->flags & PCM_MMAP) 25473b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->mmap_control->avail_min = pcm->config.avail_min; 25573b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent else 25673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->mmap_control->avail_min = 1; 257dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 258dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return 0; 259dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 260dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilsonmmap_error: 261dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 262dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr)); 263dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (!pcm->sync_ptr) 264dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return -ENOMEM; 265dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_status = &pcm->sync_ptr->s.status; 266dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_control = &pcm->sync_ptr->c.control; 26773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->flags & PCM_MMAP) 26873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->mmap_control->avail_min = pcm->config.avail_min; 26973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent else 27073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->mmap_control->avail_min = 1; 27173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 272dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm_sync_ptr(pcm, 0); 273dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 274dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return 0; 275dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson} 276dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 277dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilsonstatic void pcm_hw_munmap_status(struct pcm *pcm) { 278dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->sync_ptr) { 279dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson free(pcm->sync_ptr); 280dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->sync_ptr = NULL; 281dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson } else { 282dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson int page_size = sysconf(_SC_PAGE_SIZE); 283dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->mmap_status) 284dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson munmap(pcm->mmap_status, page_size); 285dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->mmap_control) 286dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson munmap(pcm->mmap_control, page_size); 287dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson } 288dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_status = NULL; 289dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_control = NULL; 290dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson} 291dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 292e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset, 293e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson const char *src, unsigned int src_offset, 294e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int frames) 295e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 296e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int size_bytes = pcm_frames_to_bytes(pcm, frames); 297e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset); 298e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset); 299e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 300e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* interleaved only atm */ 301e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes, 302e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson src + src_offset_bytes, size_bytes); 303e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 304e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 305e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 306e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic int pcm_mmap_write_areas(struct pcm *pcm, char *src, 307e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int offset, unsigned int size) 308e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 309e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson void *pcm_areas; 310e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int commit; 311e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int pcm_offset, frames, count = 0; 312e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 313e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson while (size > 0) { 314e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson frames = size; 315e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames); 316e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_areas_copy(pcm, pcm_offset, src, offset, frames); 317e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson commit = pcm_mmap_commit(pcm, pcm_offset, frames); 318e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (commit < 0) { 319e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson oops(pcm, commit, "failed to commit %d frames\n", frames); 320e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return commit; 321e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 322e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 323e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson offset += commit; 324e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson count += commit; 325e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson size -= commit; 326e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 327e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return count; 328e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 329e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 330dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilsonint pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail, 331dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson struct timespec *tstamp) 332dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson{ 333dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson int frames; 334dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson int rc; 335dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson snd_pcm_uframes_t hw_ptr; 336dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 337dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (!pcm_is_ready(pcm)) 338dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return -1; 339dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 340dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC); 341dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (rc < 0) 342dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return -1; 343dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 3448dd366f47258eeda07910a99b29ba681fe7464a7Simon Wilson if ((pcm->mmap_status->state != PCM_STATE_RUNNING) && 3458dd366f47258eeda07910a99b29ba681fe7464a7Simon Wilson (pcm->mmap_status->state != PCM_STATE_DRAINING)) 3465aed71db7d87ba518b53e40c00dcaa0afbc2acebSimon Wilson return -1; 3475aed71db7d87ba518b53e40c00dcaa0afbc2acebSimon Wilson 348dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson *tstamp = pcm->mmap_status->tstamp; 349dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0) 350dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return -1; 351dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 352dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson hw_ptr = pcm->mmap_status->hw_ptr; 353dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->flags & PCM_IN) 354dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson frames = hw_ptr - pcm->mmap_control->appl_ptr; 355dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson else 356dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; 357dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 358dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (frames < 0) 35973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent frames += pcm->boundary; 36073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent else if (frames > (int)pcm->boundary) 36173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent frames -= pcm->boundary; 362dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 363dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson *avail = (unsigned int)frames; 364dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 365dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return 0; 366dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson} 367dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 368edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_write(struct pcm *pcm, void *data, unsigned int count) 369edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 370edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_xferi x; 371edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 372edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm->flags & PCM_IN) 373edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return -EINVAL; 374edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 375edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson x.buf = data; 37683b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson x.frames = count / (pcm->config.channels * 37783b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson pcm_format_to_bits(pcm->config.format) / 8); 378edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 379edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (;;) { 380edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!pcm->running) { 381edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) 382edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot prepare channel"); 383edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) 384edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot write initial data"); 385edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 1; 386edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 387edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 388edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { 389edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 0; 390edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (errno == EPIPE) { 391edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson /* we failed to make our window -- try to restart */ 392edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->underruns++; 393edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson continue; 394edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 395edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot write stream data"); 396edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 397edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 398edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 399edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 400edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 401edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_read(struct pcm *pcm, void *data, unsigned int count) 402edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 403edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_xferi x; 404edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 405edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!(pcm->flags & PCM_IN)) 406edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return -EINVAL; 407edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 408edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson x.buf = data; 40983b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson x.frames = count / (pcm->config.channels * 41083b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson pcm_format_to_bits(pcm->config.format) / 8); 411edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 412edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (;;) { 413edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!pcm->running) { 414edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) 415edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot prepare channel"); 416edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) 417edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot start channel"); 418edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 1; 419edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 420edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { 421edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 0; 422edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (errno == EPIPE) { 423edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson /* we failed to make our window -- try to restart */ 424edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->underruns++; 425edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson continue; 426edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 427edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot read stream data"); 428edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 429edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 430edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 431edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 432edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 433edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic struct pcm bad_pcm = { 434edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson .fd = -1, 435edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson}; 436edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 437edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_close(struct pcm *pcm) 438edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 439edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm == &bad_pcm) 440edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 441edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 442dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm_hw_munmap_status(pcm); 443dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 444e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pcm->flags & PCM_MMAP) { 445e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_stop(pcm); 446e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size)); 447e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 448e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 449edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm->fd >= 0) 450edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson close(pcm->fd); 451edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 0; 452edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->buffer_size = 0; 453edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->fd = -1; 454dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson free(pcm); 455edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 456edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 457edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 458edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstruct pcm *pcm_open(unsigned int card, unsigned int device, 459edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson unsigned int flags, struct pcm_config *config) 460edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 461edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct pcm *pcm; 462edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_pcm_info info; 463edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_pcm_hw_params params; 464edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_pcm_sw_params sparams; 465edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson char fn[256]; 466dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson int rc; 467edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 468edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm = calloc(1, sizeof(struct pcm)); 469edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!pcm || !config) 470edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return &bad_pcm; /* TODO: could support default config here */ 471edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 472edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->config = *config; 473edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 474edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device, 475edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson flags & PCM_IN ? 'c' : 'p'); 476edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 477edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->flags = flags; 478edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->fd = open(fn, O_RDWR); 479edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm->fd < 0) { 480edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot open device '%s'", fn); 481edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm; 482edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 483edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 484edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) { 485edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot get info"); 486e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson goto fail_close; 487edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 488edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 489edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_init(¶ms); 490edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, 491edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm_format_to_alsa(config->format)); 492edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT, 493edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson SNDRV_PCM_SUBFORMAT_STD); 494edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size); 495edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 496edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm_format_to_bits(config->format)); 497edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, 498edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm_format_to_bits(config->format) * config->channels); 499edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, 500edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson config->channels); 501edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count); 502edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate); 503edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 504e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (flags & PCM_NOIRQ) { 505e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 506e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (!(flags & PCM_MMAP)) { 507e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson oops(pcm, -EINVAL, "noirq only currently supported with mmap()."); 508e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson goto fail; 509e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 510e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 511e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP; 512e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->noirq_frames_per_msec = config->rate / 1000; 513e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 514e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 515e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (flags & PCM_MMAP) 516e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, 517e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); 518e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson else 519e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, 520e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson SNDRV_PCM_ACCESS_RW_INTERLEAVED); 521e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 522edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { 523edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot set hw params"); 524e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson goto fail_close; 525edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 526edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 527e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* get our refined hw_params */ 528e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson config->period_size = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); 529e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson config->period_count = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS); 530e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->buffer_size = config->period_count * config->period_size; 531e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 532e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (flags & PCM_MMAP) { 533e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size), 534e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0); 535e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pcm->mmap_buffer == MAP_FAILED) { 536e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson oops(pcm, -errno, "failed to mmap buffer %d bytes\n", 537e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_frames_to_bytes(pcm, pcm->buffer_size)); 538e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson goto fail_close; 539e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 540e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 541e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 542e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 543edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson memset(&sparams, 0, sizeof(sparams)); 544dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE; 545edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.period_step = 1; 546c1239623c178f0142352c28a0f968d826afcb078Simon Wilson 547c1239623c178f0142352c28a0f968d826afcb078Simon Wilson if (!config->start_threshold) 548e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->config.start_threshold = sparams.start_threshold = 549e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson config->period_count * config->period_size / 2; 550c1239623c178f0142352c28a0f968d826afcb078Simon Wilson else 551c1239623c178f0142352c28a0f968d826afcb078Simon Wilson sparams.start_threshold = config->start_threshold; 552c1239623c178f0142352c28a0f968d826afcb078Simon Wilson 553e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* pick a high stop threshold - todo: does this need further tuning */ 5541b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent if (!config->stop_threshold) { 5551b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent if (pcm->flags & PCM_IN) 5561b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent pcm->config.stop_threshold = sparams.stop_threshold = 5571b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent config->period_count * config->period_size * 10; 5581b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent else 5591b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent pcm->config.stop_threshold = sparams.stop_threshold = 5601b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent config->period_count * config->period_size; 5611b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent } 562c1239623c178f0142352c28a0f968d826afcb078Simon Wilson else 563c1239623c178f0142352c28a0f968d826afcb078Simon Wilson sparams.stop_threshold = config->stop_threshold; 564c1239623c178f0142352c28a0f968d826afcb078Simon Wilson 56573b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (!pcm->config.avail_min) { 56673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->flags & PCM_MMAP) 56773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->config.avail_min = sparams.avail_min = pcm->config.period_size; 56873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent else 56973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->config.avail_min = sparams.avail_min = 1; 57073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent } else 57173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent sparams.avail_min = config->avail_min; 57273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 573edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.xfer_align = config->period_size / 2; /* needed for old kernels */ 574edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.silence_size = 0; 575c1239623c178f0142352c28a0f968d826afcb078Simon Wilson sparams.silence_threshold = config->silence_threshold; 576e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->boundary = sparams.boundary = pcm->buffer_size; 577c1239623c178f0142352c28a0f968d826afcb078Simon Wilson 578e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson while (pcm->boundary * 2 <= LONG_MAX - pcm->buffer_size) 579e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->boundary *= 2; 580edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 581edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) { 582edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot set sw params"); 583edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson goto fail; 584edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 585edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 586dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson rc = pcm_hw_mmap_status(pcm); 587dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (rc < 0) { 588dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson oops(pcm, rc, "mmap status failed"); 589dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson goto fail; 590dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson } 591dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 592edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->underruns = 0; 593edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm; 594edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 595edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonfail: 596e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (flags & PCM_MMAP) 597e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size)); 598e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonfail_close: 599edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson close(pcm->fd); 600edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->fd = -1; 601edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm; 602edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 603edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 604edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_is_ready(struct pcm *pcm) 605edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 606edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm->fd >= 0; 607edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 60870d77088678cf120d37602c66580287914781aaaSimon Wilson 60970d77088678cf120d37602c66580287914781aaaSimon Wilsonint pcm_start(struct pcm *pcm) 61070d77088678cf120d37602c66580287914781aaaSimon Wilson{ 61170d77088678cf120d37602c66580287914781aaaSimon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0) 61270d77088678cf120d37602c66580287914781aaaSimon Wilson return oops(pcm, errno, "cannot prepare channel"); 613e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 614e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pcm->flags & PCM_MMAP) 615e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_sync_ptr(pcm, 0); 616e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 61770d77088678cf120d37602c66580287914781aaaSimon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0) 61870d77088678cf120d37602c66580287914781aaaSimon Wilson return oops(pcm, errno, "cannot start channel"); 61970d77088678cf120d37602c66580287914781aaaSimon Wilson 620e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->running = 1; 62170d77088678cf120d37602c66580287914781aaaSimon Wilson return 0; 62270d77088678cf120d37602c66580287914781aaaSimon Wilson} 62370d77088678cf120d37602c66580287914781aaaSimon Wilson 62470d77088678cf120d37602c66580287914781aaaSimon Wilsonint pcm_stop(struct pcm *pcm) 62570d77088678cf120d37602c66580287914781aaaSimon Wilson{ 62670d77088678cf120d37602c66580287914781aaaSimon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0) 62770d77088678cf120d37602c66580287914781aaaSimon Wilson return oops(pcm, errno, "cannot stop channel"); 62870d77088678cf120d37602c66580287914781aaaSimon Wilson 629e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->running = 0; 630e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 631e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 632e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 633e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic inline int pcm_mmap_playback_avail(struct pcm *pcm) 634e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 635e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int avail; 636e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 637e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; 638e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 639e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (avail < 0) 640e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail += pcm->boundary; 641e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson else if (avail > (int)pcm->boundary) 642e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail -= pcm->boundary; 643e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 644e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return avail; 645e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 646e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 647e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic inline int pcm_mmap_capture_avail(struct pcm *pcm) 648e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 649e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr; 650e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (avail < 0) 651e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail += pcm->boundary; 652e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return avail; 653e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 654e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 655e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic inline int pcm_mmap_avail(struct pcm *pcm) 656e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 657e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC); 658e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pcm->flags & PCM_IN) 659e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return pcm_mmap_capture_avail(pcm); 660e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson else 661e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return pcm_mmap_playback_avail(pcm); 662e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 663e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 664e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic void pcm_mmap_appl_forward(struct pcm *pcm, int frames) 665e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 666e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int appl_ptr = pcm->mmap_control->appl_ptr; 667e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson appl_ptr += frames; 668e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 669e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* check for boundary wrap */ 670e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (appl_ptr > pcm->boundary) 671e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson appl_ptr -= pcm->boundary; 672e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->mmap_control->appl_ptr = appl_ptr; 673e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 674e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 675e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, 676e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int *frames) 677e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 678e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int continuous, copy_frames, avail; 679e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 680e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* return the mmap buffer */ 681e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson *areas = pcm->mmap_buffer; 682e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 683e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* and the application offset in frames */ 684e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size; 685e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 686e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail = pcm_mmap_avail(pcm); 687e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (avail > pcm->buffer_size) 688e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail = pcm->buffer_size; 689e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson continuous = pcm->buffer_size - *offset; 690e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 691e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* we can only copy frames if the are availabale and continuos */ 692e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson copy_frames = *frames; 693e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (copy_frames > avail) 694e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson copy_frames = avail; 695e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (copy_frames > continuous) 696e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson copy_frames = continuous; 697e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson *frames = copy_frames; 698e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 69970d77088678cf120d37602c66580287914781aaaSimon Wilson return 0; 70070d77088678cf120d37602c66580287914781aaaSimon Wilson} 70170d77088678cf120d37602c66580287914781aaaSimon Wilson 702e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames) 703e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 704e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* update the application pointer in userspace and kernel */ 705e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_mmap_appl_forward(pcm, frames); 706e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_sync_ptr(pcm, 0); 707e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 708e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return frames; 709e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 710e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 711e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_avail_update(struct pcm *pcm) 712e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 713e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_sync_ptr(pcm, 0); 714e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return pcm_mmap_avail(pcm); 715e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 716e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 717e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_state(struct pcm *pcm) 718e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 719e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int err = pcm_sync_ptr(pcm, 0); 720e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (err < 0) 721e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return err; 722e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 723e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return pcm->mmap_status->state; 724e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 725e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 72673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurentint pcm_set_avail_min(struct pcm *pcm, int avail_min) 72773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent{ 72873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if ((~pcm->flags) & (PCM_MMAP | PCM_NOIRQ)) 72973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent return -ENOSYS; 73073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 73173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->config.avail_min = avail_min; 73273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent return 0; 73373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent} 73473b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 735e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_wait(struct pcm *pcm, int timeout) 736e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 737e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson struct pollfd pfd; 738e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned short revents = 0; 739e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int err; 740e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 741e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pfd.fd = pcm->fd; 742e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pfd.events = POLLOUT | POLLERR | POLLNVAL; 743e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 744e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson do { 745e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* let's wait for avail or timeout */ 746e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson err = poll(&pfd, 1, timeout); 747e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (err < 0) 748e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -errno; 749e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 750e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* timeout ? */ 751e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (err == 0) 752e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 753e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 754e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* have we been interrupted ? */ 755e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (errno == -EINTR) 756e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson continue; 757e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 758e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* check for any errors */ 759e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pfd.revents & (POLLERR | POLLNVAL)) { 760e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson switch (pcm_state(pcm)) { 761e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson case PCM_STATE_XRUN: 762e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -EPIPE; 763e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson case PCM_STATE_SUSPENDED: 764e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -ESTRPIPE; 765e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson case PCM_STATE_DISCONNECTED: 766e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -ENODEV; 767e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson default: 768e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -EIO; 769e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 770e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 771e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* poll again if fd not ready for IO */ 772e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } while (!(pfd.revents & (POLLIN | POLLOUT))); 773e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 774e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 1; 775e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 776e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 777e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_mmap_write(struct pcm *pcm, void *buffer, unsigned int bytes) 778e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 779e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int err = 0, frames, avail; 780e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int offset = 0, count; 781e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 782e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (bytes == 0) 783e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 784e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 785e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson count = pcm_bytes_to_frames(pcm, bytes); 786e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 787e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson while (count > 0) { 788e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 789e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* get the available space for writing new frames */ 790e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail = pcm_avail_update(pcm); 791e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (avail < 0) { 792e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson fprintf(stderr, "cannot determine available mmap frames"); 793e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return err; 794e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 795e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 796e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* start the audio if we reach the threshold */ 797e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (!pcm->running && 798e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (pcm->buffer_size - avail) >= pcm->config.start_threshold) { 799e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pcm_start(pcm) < 0) { 800e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n", 801e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (unsigned int)pcm->mmap_status->hw_ptr, 802e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (unsigned int)pcm->mmap_control->appl_ptr, 803e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail); 804e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -errno; 805e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 80673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->wait_for_avail_min = 0; 807e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 808e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 809e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* sleep until we have space to write new frames */ 81073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->running) { 81173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent /* enable waiting for avail_min threshold when less frames than we have to write 81273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent * are available. */ 81373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (!pcm->wait_for_avail_min && (count > (unsigned int)avail)) 81473b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->wait_for_avail_min = 1; 81573b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 81673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->wait_for_avail_min && (avail < pcm->config.avail_min)) { 81773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent int time = -1; 81873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 81973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent /* disable waiting for avail_min threshold to allow small amounts of data to be 82073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent * written without waiting as long as there is enough room in buffer. */ 82173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->wait_for_avail_min = 0; 82273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 82373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->flags & PCM_NOIRQ) 82473b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent time = (pcm->config.avail_min - avail) / pcm->noirq_frames_per_msec; 82573b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 82673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent err = pcm_wait(pcm, time); 82773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (err < 0) { 82873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->running = 0; 82973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent oops(pcm, err, "wait error: hw 0x%x app 0x%x avail 0x%x\n", 83073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent (unsigned int)pcm->mmap_status->hw_ptr, 83173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent (unsigned int)pcm->mmap_control->appl_ptr, 83273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent avail); 83373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->mmap_control->appl_ptr = 0; 83473b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent return err; 83573b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent } 83673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent continue; 837e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 838e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 839e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 840e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson frames = count; 841e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (frames > avail) 842e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson frames = avail; 843e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 844e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (!frames) 845e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson break; 846e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 847e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* copy frames from buffer */ 848e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson frames = pcm_mmap_write_areas(pcm, buffer, offset, frames); 849e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (frames < 0) { 850e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n", 851e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (unsigned int)pcm->mmap_status->hw_ptr, 852e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (unsigned int)pcm->mmap_control->appl_ptr, 853e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail); 854e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return frames; 855e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 856e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 857e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson offset += frames; 858e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson count -= frames; 859e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 860e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 861e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson_end: 862e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 863e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 864