pcm.c revision 6b0a206624ebe78cc10dba7015438371d1506de5
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 9642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilsonstatic unsigned int param_get_min(struct snd_pcm_hw_params *p, int n) 9742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson{ 9842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson if (param_is_interval(n)) { 9942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson struct snd_interval *i = param_to_interval(p, n); 10042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return i->min; 10142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson } 10242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return 0; 10342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson} 10442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 10542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilsonstatic unsigned int param_get_max(struct snd_pcm_hw_params *p, int n) 10642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson{ 10742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson if (param_is_interval(n)) { 10842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson struct snd_interval *i = param_to_interval(p, n); 10942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return i->max; 11042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson } 11142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return 0; 11242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson} 11342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 114edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val) 115edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 116edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (param_is_interval(n)) { 117edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_interval *i = param_to_interval(p, n); 118edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->min = val; 119edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->max = val; 120edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->integer = 1; 121edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 122edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 123edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 124e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic unsigned int param_get_int(struct snd_pcm_hw_params *p, int n) 125e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 126e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (param_is_interval(n)) { 127e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson struct snd_interval *i = param_to_interval(p, n); 128e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (i->integer) 129e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return i->max; 130e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 131e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 132e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 133e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 134edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_init(struct snd_pcm_hw_params *p) 135edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 136edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int n; 137edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 138edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson memset(p, 0, sizeof(*p)); 139edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; 140edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { 141edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_mask *m = param_to_mask(p, n); 142edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[0] = ~0; 143edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[1] = ~0; 144edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 145edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; 146edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { 147edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_interval *i = param_to_interval(p, n); 148edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->min = 0; 149edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->max = ~0; 150edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 15142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson p->rmask = ~0U; 15242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson p->cmask = 0; 15342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson p->info = ~0U; 154edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 155edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 156edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define PCM_ERROR_MAX 128 157edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 158edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstruct pcm { 159edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int fd; 160edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson unsigned int flags; 161edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int running:1; 162edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int underruns; 163edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson unsigned int buffer_size; 164e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int boundary; 165edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson char error[PCM_ERROR_MAX]; 166edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct pcm_config config; 167dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson struct snd_pcm_mmap_status *mmap_status; 168dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson struct snd_pcm_mmap_control *mmap_control; 169dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson struct snd_pcm_sync_ptr *sync_ptr; 170e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson void *mmap_buffer; 171e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int noirq_frames_per_msec; 17273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent int wait_for_avail_min; 173edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson}; 174edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 175edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonunsigned int pcm_get_buffer_size(struct pcm *pcm) 176edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 177edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm->buffer_size; 178edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 179edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 180edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonconst char* pcm_get_error(struct pcm *pcm) 181edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 182edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm->error; 183edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 184edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 185edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic int oops(struct pcm *pcm, int e, const char *fmt, ...) 186edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 187edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson va_list ap; 188edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int sz; 189edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 190edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson va_start(ap, fmt); 191edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap); 192edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson va_end(ap); 193edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sz = strlen(pcm->error); 194edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 195edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (errno) 196edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson snprintf(pcm->error + sz, PCM_ERROR_MAX - sz, 197edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson ": %s", strerror(e)); 198edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return -1; 199edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 200edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 20183b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilsonstatic unsigned int pcm_format_to_alsa(enum pcm_format format) 20283b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson{ 20383b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson switch (format) { 20483b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson case PCM_FORMAT_S32_LE: 20583b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson return SNDRV_PCM_FORMAT_S32_LE; 206da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson case PCM_FORMAT_S8: 207da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson return SNDRV_PCM_FORMAT_S8; 208da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson case PCM_FORMAT_S24_LE: 209da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson return SNDRV_PCM_FORMAT_S24_LE; 21083b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson default: 21183b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson case PCM_FORMAT_S16_LE: 21283b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson return SNDRV_PCM_FORMAT_S16_LE; 21383b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson }; 21483b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson} 21583b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson 21636ea2d824e5d8af550c139da9da20e73f82a9ae1Simon Wilsonunsigned int pcm_format_to_bits(enum pcm_format format) 21783b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson{ 21883b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson switch (format) { 21983b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson case PCM_FORMAT_S32_LE: 22036ea2d824e5d8af550c139da9da20e73f82a9ae1Simon Wilson case PCM_FORMAT_S24_LE: 22183b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson return 32; 22283b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson default: 22383b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson case PCM_FORMAT_S16_LE: 22483b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson return 16; 22583b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson }; 22683b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson} 22783b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson 228e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonunsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes) 229e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 230e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return bytes / (pcm->config.channels * 231e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (pcm_format_to_bits(pcm->config.format) >> 3)); 232e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 233e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 234e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonunsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames) 235e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 236e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return frames * pcm->config.channels * 237e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (pcm_format_to_bits(pcm->config.format) >> 3); 238e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 239e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 240dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilsonstatic int pcm_sync_ptr(struct pcm *pcm, int flags) { 241dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->sync_ptr) { 242dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->sync_ptr->flags = flags; 243dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0) 244dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return -1; 245dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson } 246dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return 0; 247dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson} 248dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 249dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilsonstatic int pcm_hw_mmap_status(struct pcm *pcm) { 250dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 251dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->sync_ptr) 252dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return 0; 253dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 254dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson int page_size = sysconf(_SC_PAGE_SIZE); 255dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED, 256dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS); 257dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->mmap_status == MAP_FAILED) 258dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_status = NULL; 259dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (!pcm->mmap_status) 260dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson goto mmap_error; 261dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 262dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE, 263dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL); 264dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->mmap_control == MAP_FAILED) 265dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_control = NULL; 266dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (!pcm->mmap_control) { 267dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson munmap(pcm->mmap_status, page_size); 268dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_status = NULL; 269dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson goto mmap_error; 270dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson } 27173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->flags & PCM_MMAP) 27273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->mmap_control->avail_min = pcm->config.avail_min; 27373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent else 27473b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->mmap_control->avail_min = 1; 275dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 276dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return 0; 277dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 278dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilsonmmap_error: 279dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 280dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr)); 281dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (!pcm->sync_ptr) 282dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return -ENOMEM; 283dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_status = &pcm->sync_ptr->s.status; 284dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_control = &pcm->sync_ptr->c.control; 28573b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->flags & PCM_MMAP) 28673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->mmap_control->avail_min = pcm->config.avail_min; 28773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent else 28873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->mmap_control->avail_min = 1; 28973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 290dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm_sync_ptr(pcm, 0); 291dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 292dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return 0; 293dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson} 294dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 295dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilsonstatic void pcm_hw_munmap_status(struct pcm *pcm) { 296dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->sync_ptr) { 297dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson free(pcm->sync_ptr); 298dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->sync_ptr = NULL; 299dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson } else { 300dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson int page_size = sysconf(_SC_PAGE_SIZE); 301dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->mmap_status) 302dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson munmap(pcm->mmap_status, page_size); 303dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->mmap_control) 304dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson munmap(pcm->mmap_control, page_size); 305dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson } 306dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_status = NULL; 307dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm->mmap_control = NULL; 308dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson} 309dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 310e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset, 311e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson const char *src, unsigned int src_offset, 312e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int frames) 313e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 314e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int size_bytes = pcm_frames_to_bytes(pcm, frames); 315e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset); 316e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset); 317e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 318e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* interleaved only atm */ 319e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes, 320e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson src + src_offset_bytes, size_bytes); 321e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 322e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 323e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 324daa83291944318d8face12c780dfb69ae96b0723Simon Wilsonstatic int pcm_mmap_write_areas(struct pcm *pcm, const char *src, 325e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int offset, unsigned int size) 326e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 327e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson void *pcm_areas; 328e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int commit; 329e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int pcm_offset, frames, count = 0; 330e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 331e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson while (size > 0) { 332e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson frames = size; 333e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames); 334e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_areas_copy(pcm, pcm_offset, src, offset, frames); 335e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson commit = pcm_mmap_commit(pcm, pcm_offset, frames); 336e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (commit < 0) { 337e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson oops(pcm, commit, "failed to commit %d frames\n", frames); 338e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return commit; 339e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 340e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 341e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson offset += commit; 342e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson count += commit; 343e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson size -= commit; 344e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 345e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return count; 346e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 347e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 348dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilsonint pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail, 349dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson struct timespec *tstamp) 350dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson{ 351dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson int frames; 352dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson int rc; 353dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson snd_pcm_uframes_t hw_ptr; 354dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 355dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (!pcm_is_ready(pcm)) 356dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return -1; 357dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 358dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC); 359dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (rc < 0) 360dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return -1; 361dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 3628dd366f47258eeda07910a99b29ba681fe7464a7Simon Wilson if ((pcm->mmap_status->state != PCM_STATE_RUNNING) && 3638dd366f47258eeda07910a99b29ba681fe7464a7Simon Wilson (pcm->mmap_status->state != PCM_STATE_DRAINING)) 3645aed71db7d87ba518b53e40c00dcaa0afbc2acebSimon Wilson return -1; 3655aed71db7d87ba518b53e40c00dcaa0afbc2acebSimon Wilson 366dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson *tstamp = pcm->mmap_status->tstamp; 367dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0) 368dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return -1; 369dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 370dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson hw_ptr = pcm->mmap_status->hw_ptr; 371dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (pcm->flags & PCM_IN) 372dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson frames = hw_ptr - pcm->mmap_control->appl_ptr; 373dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson else 374dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; 375dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 376dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (frames < 0) 37773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent frames += pcm->boundary; 37873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent else if (frames > (int)pcm->boundary) 37973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent frames -= pcm->boundary; 380dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 381dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson *avail = (unsigned int)frames; 382dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 383dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson return 0; 384dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson} 385dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 386daa83291944318d8face12c780dfb69ae96b0723Simon Wilsonint pcm_write(struct pcm *pcm, const void *data, unsigned int count) 387edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 388edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_xferi x; 389edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 390edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm->flags & PCM_IN) 391edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return -EINVAL; 392edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 393daa83291944318d8face12c780dfb69ae96b0723Simon Wilson x.buf = (void*)data; 39483b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson x.frames = count / (pcm->config.channels * 39583b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson pcm_format_to_bits(pcm->config.format) / 8); 396edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 397edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (;;) { 398edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!pcm->running) { 399edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) 400edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot prepare channel"); 401edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) 402edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot write initial data"); 403edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 1; 404edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 405edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 406edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { 407edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 0; 408edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (errno == EPIPE) { 409673253acf06838bb24d0b0b1a3962bed573855b8John Grossman /* we failed to make our window -- try to restart if we are 410673253acf06838bb24d0b0b1a3962bed573855b8John Grossman * allowed to do so. Otherwise, simply allow the EPIPE error to 411673253acf06838bb24d0b0b1a3962bed573855b8John Grossman * propagate up to the app level */ 412edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->underruns++; 413673253acf06838bb24d0b0b1a3962bed573855b8John Grossman if (pcm->flags & PCM_NORESTART) 414673253acf06838bb24d0b0b1a3962bed573855b8John Grossman return -EPIPE; 415edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson continue; 416edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 417edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot write stream data"); 418edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 419edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 420edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 421edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 422edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 423edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_read(struct pcm *pcm, void *data, unsigned int count) 424edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 425edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_xferi x; 426edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 427edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!(pcm->flags & PCM_IN)) 428edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return -EINVAL; 429edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 430edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson x.buf = data; 43183b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson x.frames = count / (pcm->config.channels * 43283b1d6de55ec69cde7a935d6ada0011669d88ed6Simon Wilson pcm_format_to_bits(pcm->config.format) / 8); 433edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 434edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (;;) { 435edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!pcm->running) { 43685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson if (pcm_start(pcm) < 0) { 43785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson fprintf(stderr, "start error"); 43885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson return -errno; 43985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson } 440edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 441edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { 442edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 0; 443edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (errno == EPIPE) { 444edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson /* we failed to make our window -- try to restart */ 445edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->underruns++; 446edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson continue; 447edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 448edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot read stream data"); 449edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 450edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 451edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 452edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 453edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 454edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic struct pcm bad_pcm = { 455edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson .fd = -1, 456edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson}; 457edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 45842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilsonstruct pcm_params *pcm_params_get(unsigned int card, unsigned int device, 45942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson unsigned int flags) 46042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson{ 46142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson struct snd_pcm_hw_params *params; 46242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson char fn[256]; 46342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson int fd; 46442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 46542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device, 46642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson flags & PCM_IN ? 'c' : 'p'); 46742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 46842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson fd = open(fn, O_RDWR); 46942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson if (fd < 0) { 47042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson fprintf(stderr, "cannot open device '%s'\n", fn); 47142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson goto err_open; 47242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson } 47342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 47442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson params = calloc(1, sizeof(struct snd_pcm_hw_params)); 47542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson if (!params) 47642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson goto err_calloc; 47742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 47842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson param_init(params); 47942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) { 48042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno); 48142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson goto err_hw_refine; 48242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson } 48342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 48442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson close(fd); 48542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 48642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return (struct pcm_params *)params; 48742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 48842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilsonerr_hw_refine: 48942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson free(params); 49042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilsonerr_calloc: 49142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson close(fd); 49242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilsonerr_open: 49342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return NULL; 49442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson} 49542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 49642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilsonvoid pcm_params_free(struct pcm_params *pcm_params) 49742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson{ 49842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; 49942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 50042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson if (params) 50142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson free(params); 50242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson} 50342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 50442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilsonstatic int pcm_param_to_alsa(enum pcm_param param) 50542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson{ 50642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson switch (param) { 50742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_SAMPLE_BITS: 50842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_SAMPLE_BITS; 50942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 51042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_FRAME_BITS: 51142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_FRAME_BITS; 51242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 51342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_CHANNELS: 51442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_CHANNELS; 51542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 51642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_RATE: 51742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_RATE; 51842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 51942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_PERIOD_TIME: 52042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_PERIOD_TIME; 52142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 52242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_PERIOD_SIZE: 52342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_PERIOD_SIZE; 52442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 52542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_PERIOD_BYTES: 52642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_PERIOD_BYTES; 52742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 52842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_PERIODS: 52942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_PERIODS; 53042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 53142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_BUFFER_TIME: 53242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_BUFFER_TIME; 53342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 53442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_BUFFER_SIZE: 53542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_BUFFER_SIZE; 53642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 53742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_BUFFER_BYTES: 53842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_BUFFER_BYTES; 53942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 54042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson case PCM_PARAM_TICK_TIME: 54142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return SNDRV_PCM_HW_PARAM_TICK_TIME; 54242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson break; 54342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 54442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson default: 54542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return -1; 54642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson } 54742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson} 54842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 54942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilsonunsigned int pcm_params_get_min(struct pcm_params *pcm_params, 55042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson enum pcm_param param) 55142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson{ 55242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; 55342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson int p; 55442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 55542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson if (!params) 55642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return 0; 55742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 55842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson p = pcm_param_to_alsa(param); 55942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson if (p < 0) 56042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return 0; 56142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 56242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return param_get_min(params, p); 56342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson} 56442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 56542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilsonunsigned int pcm_params_get_max(struct pcm_params *pcm_params, 56642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson enum pcm_param param) 56742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson{ 56842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; 56942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson int p; 57042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 57142fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson if (!params) 57242fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return 0; 57342fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 57442fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson p = pcm_param_to_alsa(param); 57542fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson if (p < 0) 57642fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return 0; 57742fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 57842fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson return param_get_max(params, p); 57942fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson} 58042fc2d393c868d5db609e39551d64f1e60fefa0eSimon Wilson 581edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_close(struct pcm *pcm) 582edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 583edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm == &bad_pcm) 584edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 585edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 586dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson pcm_hw_munmap_status(pcm); 587dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 588e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pcm->flags & PCM_MMAP) { 589e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_stop(pcm); 590e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size)); 591e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 592e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 593edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm->fd >= 0) 594edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson close(pcm->fd); 595edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 0; 596edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->buffer_size = 0; 597edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->fd = -1; 598dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson free(pcm); 599edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 600edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 601edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 602edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstruct pcm *pcm_open(unsigned int card, unsigned int device, 603edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson unsigned int flags, struct pcm_config *config) 604edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 605edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct pcm *pcm; 606edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_pcm_info info; 607edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_pcm_hw_params params; 608edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_pcm_sw_params sparams; 609edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson char fn[256]; 610dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson int rc; 611edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 612edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm = calloc(1, sizeof(struct pcm)); 613edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!pcm || !config) 614edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return &bad_pcm; /* TODO: could support default config here */ 615edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 616edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->config = *config; 617edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 618edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device, 619edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson flags & PCM_IN ? 'c' : 'p'); 620edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 621edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->flags = flags; 622edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->fd = open(fn, O_RDWR); 623edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm->fd < 0) { 624edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot open device '%s'", fn); 625edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm; 626edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 627edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 628edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) { 629edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot get info"); 630e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson goto fail_close; 631edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 632edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 633edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_init(¶ms); 634edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, 635edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm_format_to_alsa(config->format)); 636edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT, 637edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson SNDRV_PCM_SUBFORMAT_STD); 638edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size); 639edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 640edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm_format_to_bits(config->format)); 641edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, 642edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm_format_to_bits(config->format) * config->channels); 643edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, 644edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson config->channels); 645edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count); 646edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate); 647edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 648e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (flags & PCM_NOIRQ) { 649e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 650e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (!(flags & PCM_MMAP)) { 651e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson oops(pcm, -EINVAL, "noirq only currently supported with mmap()."); 652e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson goto fail; 653e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 654e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 655e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP; 656e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->noirq_frames_per_msec = config->rate / 1000; 657e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 658e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 659e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (flags & PCM_MMAP) 660e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, 661e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); 662e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson else 663e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, 664e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson SNDRV_PCM_ACCESS_RW_INTERLEAVED); 665e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 666edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { 667edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot set hw params"); 668e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson goto fail_close; 669edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 670edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 671e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* get our refined hw_params */ 672e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson config->period_size = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); 673e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson config->period_count = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS); 674e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->buffer_size = config->period_count * config->period_size; 675e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 676e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (flags & PCM_MMAP) { 677e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size), 678e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0); 679e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pcm->mmap_buffer == MAP_FAILED) { 680e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson oops(pcm, -errno, "failed to mmap buffer %d bytes\n", 681e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_frames_to_bytes(pcm, pcm->buffer_size)); 682e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson goto fail_close; 683e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 684e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 685e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 686e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 687edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson memset(&sparams, 0, sizeof(sparams)); 688dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE; 689edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.period_step = 1; 690c1239623c178f0142352c28a0f968d826afcb078Simon Wilson 691ff2e54293f32b5bb5ff75b4cb25babb00f429ca4Eric Laurent if (!config->start_threshold) { 692ff2e54293f32b5bb5ff75b4cb25babb00f429ca4Eric Laurent if (pcm->flags & PCM_IN) 693ff2e54293f32b5bb5ff75b4cb25babb00f429ca4Eric Laurent pcm->config.start_threshold = sparams.start_threshold = 1; 694ff2e54293f32b5bb5ff75b4cb25babb00f429ca4Eric Laurent else 695ff2e54293f32b5bb5ff75b4cb25babb00f429ca4Eric Laurent pcm->config.start_threshold = sparams.start_threshold = 696ff2e54293f32b5bb5ff75b4cb25babb00f429ca4Eric Laurent config->period_count * config->period_size / 2; 697ff2e54293f32b5bb5ff75b4cb25babb00f429ca4Eric Laurent } else 698c1239623c178f0142352c28a0f968d826afcb078Simon Wilson sparams.start_threshold = config->start_threshold; 699c1239623c178f0142352c28a0f968d826afcb078Simon Wilson 700e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* pick a high stop threshold - todo: does this need further tuning */ 7011b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent if (!config->stop_threshold) { 7021b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent if (pcm->flags & PCM_IN) 7031b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent pcm->config.stop_threshold = sparams.stop_threshold = 7041b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent config->period_count * config->period_size * 10; 7051b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent else 7061b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent pcm->config.stop_threshold = sparams.stop_threshold = 7071b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent config->period_count * config->period_size; 7081b32ddfd35e54b0a6ef1e43ca35dbc1547e5676aEric Laurent } 709c1239623c178f0142352c28a0f968d826afcb078Simon Wilson else 710c1239623c178f0142352c28a0f968d826afcb078Simon Wilson sparams.stop_threshold = config->stop_threshold; 711c1239623c178f0142352c28a0f968d826afcb078Simon Wilson 71273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (!pcm->config.avail_min) { 71373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->flags & PCM_MMAP) 71473b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->config.avail_min = sparams.avail_min = pcm->config.period_size; 71573b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent else 71673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->config.avail_min = sparams.avail_min = 1; 71773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent } else 71873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent sparams.avail_min = config->avail_min; 71973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 720edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.xfer_align = config->period_size / 2; /* needed for old kernels */ 721edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.silence_size = 0; 722c1239623c178f0142352c28a0f968d826afcb078Simon Wilson sparams.silence_threshold = config->silence_threshold; 723e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->boundary = sparams.boundary = pcm->buffer_size; 724c1239623c178f0142352c28a0f968d826afcb078Simon Wilson 725daa83291944318d8face12c780dfb69ae96b0723Simon Wilson while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size) 726e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->boundary *= 2; 727edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 728edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) { 729edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot set sw params"); 730edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson goto fail; 731edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 732edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 733dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson rc = pcm_hw_mmap_status(pcm); 734dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson if (rc < 0) { 735dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson oops(pcm, rc, "mmap status failed"); 736dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson goto fail; 737dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson } 738dd88f13d9b398c132e3358c62137ff2e23f321abSimon Wilson 7396b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten#ifdef SNDRV_PCM_IOCTL_TTSTAMP 7406b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten if (pcm->flags & PCM_MONOTONIC) { 7416b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; 7426b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg); 7436b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten if (rc < 0) { 7446b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten oops(pcm, rc, "cannot set timestamp type"); 7456b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten goto fail; 7466b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten } 7476b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten } 7486b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten#endif 7496b0a206624ebe78cc10dba7015438371d1506de5Glenn Kasten 750edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->underruns = 0; 751edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm; 752edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 753edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonfail: 754e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (flags & PCM_MMAP) 755e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size)); 756e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonfail_close: 757edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson close(pcm->fd); 758edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->fd = -1; 759edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm; 760edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 761edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 762edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_is_ready(struct pcm *pcm) 763edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 764edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm->fd >= 0; 765edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 76670d77088678cf120d37602c66580287914781aaaSimon Wilson 76770d77088678cf120d37602c66580287914781aaaSimon Wilsonint pcm_start(struct pcm *pcm) 76870d77088678cf120d37602c66580287914781aaaSimon Wilson{ 76970d77088678cf120d37602c66580287914781aaaSimon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0) 77070d77088678cf120d37602c66580287914781aaaSimon Wilson return oops(pcm, errno, "cannot prepare channel"); 771e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 772e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pcm->flags & PCM_MMAP) 773e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_sync_ptr(pcm, 0); 774e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 77570d77088678cf120d37602c66580287914781aaaSimon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0) 77670d77088678cf120d37602c66580287914781aaaSimon Wilson return oops(pcm, errno, "cannot start channel"); 77770d77088678cf120d37602c66580287914781aaaSimon Wilson 778e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->running = 1; 77970d77088678cf120d37602c66580287914781aaaSimon Wilson return 0; 78070d77088678cf120d37602c66580287914781aaaSimon Wilson} 78170d77088678cf120d37602c66580287914781aaaSimon Wilson 78270d77088678cf120d37602c66580287914781aaaSimon Wilsonint pcm_stop(struct pcm *pcm) 78370d77088678cf120d37602c66580287914781aaaSimon Wilson{ 78470d77088678cf120d37602c66580287914781aaaSimon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0) 78570d77088678cf120d37602c66580287914781aaaSimon Wilson return oops(pcm, errno, "cannot stop channel"); 78670d77088678cf120d37602c66580287914781aaaSimon Wilson 787e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->running = 0; 788e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 789e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 790e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 791e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic inline int pcm_mmap_playback_avail(struct pcm *pcm) 792e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 793e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int avail; 794e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 795e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; 796e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 797e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (avail < 0) 798e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail += pcm->boundary; 799e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson else if (avail > (int)pcm->boundary) 800e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail -= pcm->boundary; 801e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 802e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return avail; 803e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 804e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 805e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic inline int pcm_mmap_capture_avail(struct pcm *pcm) 806e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 807e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr; 808e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (avail < 0) 809e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail += pcm->boundary; 810e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return avail; 811e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 812e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 813e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic inline int pcm_mmap_avail(struct pcm *pcm) 814e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 815e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC); 816e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pcm->flags & PCM_IN) 817e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return pcm_mmap_capture_avail(pcm); 818e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson else 819e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return pcm_mmap_playback_avail(pcm); 820e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 821e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 822e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonstatic void pcm_mmap_appl_forward(struct pcm *pcm, int frames) 823e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 824e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int appl_ptr = pcm->mmap_control->appl_ptr; 825e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson appl_ptr += frames; 826e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 827e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* check for boundary wrap */ 828e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (appl_ptr > pcm->boundary) 829e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson appl_ptr -= pcm->boundary; 830e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm->mmap_control->appl_ptr = appl_ptr; 831e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 832e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 833e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, 834e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int *frames) 835e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 836e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int continuous, copy_frames, avail; 837e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 838e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* return the mmap buffer */ 839e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson *areas = pcm->mmap_buffer; 840e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 841e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* and the application offset in frames */ 842e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size; 843e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 844e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail = pcm_mmap_avail(pcm); 845e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (avail > pcm->buffer_size) 846e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail = pcm->buffer_size; 847e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson continuous = pcm->buffer_size - *offset; 848e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 849e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* we can only copy frames if the are availabale and continuos */ 850e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson copy_frames = *frames; 851e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (copy_frames > avail) 852e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson copy_frames = avail; 853e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (copy_frames > continuous) 854e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson copy_frames = continuous; 855e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson *frames = copy_frames; 856e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 85770d77088678cf120d37602c66580287914781aaaSimon Wilson return 0; 85870d77088678cf120d37602c66580287914781aaaSimon Wilson} 85970d77088678cf120d37602c66580287914781aaaSimon Wilson 860e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames) 861e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 862e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* update the application pointer in userspace and kernel */ 863e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_mmap_appl_forward(pcm, frames); 864e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_sync_ptr(pcm, 0); 865e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 866e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return frames; 867e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 868e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 869e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_avail_update(struct pcm *pcm) 870e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 871e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pcm_sync_ptr(pcm, 0); 872e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return pcm_mmap_avail(pcm); 873e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 874e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 875e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_state(struct pcm *pcm) 876e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 877e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int err = pcm_sync_ptr(pcm, 0); 878e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (err < 0) 879e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return err; 880e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 881e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return pcm->mmap_status->state; 882e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 883e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 88473b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurentint pcm_set_avail_min(struct pcm *pcm, int avail_min) 88573b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent{ 88673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if ((~pcm->flags) & (PCM_MMAP | PCM_NOIRQ)) 88773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent return -ENOSYS; 88873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 88973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->config.avail_min = avail_min; 89073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent return 0; 89173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent} 89273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 893e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilsonint pcm_wait(struct pcm *pcm, int timeout) 894e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 895e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson struct pollfd pfd; 896e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int err; 897e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 898e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pfd.fd = pcm->fd; 899e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson pfd.events = POLLOUT | POLLERR | POLLNVAL; 900e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 901e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson do { 902e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* let's wait for avail or timeout */ 903e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson err = poll(&pfd, 1, timeout); 904e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (err < 0) 905e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -errno; 906e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 907e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* timeout ? */ 908e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (err == 0) 909e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 910e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 911e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* have we been interrupted ? */ 912e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (errno == -EINTR) 913e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson continue; 914e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 915e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* check for any errors */ 916e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pfd.revents & (POLLERR | POLLNVAL)) { 917e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson switch (pcm_state(pcm)) { 918e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson case PCM_STATE_XRUN: 919e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -EPIPE; 920e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson case PCM_STATE_SUSPENDED: 921e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -ESTRPIPE; 922e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson case PCM_STATE_DISCONNECTED: 923e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -ENODEV; 924e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson default: 925e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -EIO; 926e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 927e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 928e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* poll again if fd not ready for IO */ 929e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } while (!(pfd.revents & (POLLIN | POLLOUT))); 930e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 931e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 1; 932e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 933e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 934daa83291944318d8face12c780dfb69ae96b0723Simon Wilsonint pcm_mmap_write(struct pcm *pcm, const void *buffer, unsigned int bytes) 935e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson{ 936e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson int err = 0, frames, avail; 937e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson unsigned int offset = 0, count; 938e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 939e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (bytes == 0) 940e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 941e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 942e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson count = pcm_bytes_to_frames(pcm, bytes); 943e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 944e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson while (count > 0) { 945e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 946e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* get the available space for writing new frames */ 947e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail = pcm_avail_update(pcm); 948e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (avail < 0) { 949e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson fprintf(stderr, "cannot determine available mmap frames"); 950e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return err; 951e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 952e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 953e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* start the audio if we reach the threshold */ 954e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (!pcm->running && 955e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (pcm->buffer_size - avail) >= pcm->config.start_threshold) { 956e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (pcm_start(pcm) < 0) { 957e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n", 958e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (unsigned int)pcm->mmap_status->hw_ptr, 959e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (unsigned int)pcm->mmap_control->appl_ptr, 960e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail); 961e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return -errno; 962e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 96373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->wait_for_avail_min = 0; 964e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 965e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 966e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* sleep until we have space to write new frames */ 96773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->running) { 96873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent /* enable waiting for avail_min threshold when less frames than we have to write 96973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent * are available. */ 97073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (!pcm->wait_for_avail_min && (count > (unsigned int)avail)) 97173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->wait_for_avail_min = 1; 97273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 97373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->wait_for_avail_min && (avail < pcm->config.avail_min)) { 97473b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent int time = -1; 97573b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 97673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent /* disable waiting for avail_min threshold to allow small amounts of data to be 97773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent * written without waiting as long as there is enough room in buffer. */ 97873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->wait_for_avail_min = 0; 97973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 98073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (pcm->flags & PCM_NOIRQ) 98173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent time = (pcm->config.avail_min - avail) / pcm->noirq_frames_per_msec; 98273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent 98373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent err = pcm_wait(pcm, time); 98473b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent if (err < 0) { 98573b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->running = 0; 98673b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent oops(pcm, err, "wait error: hw 0x%x app 0x%x avail 0x%x\n", 98773b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent (unsigned int)pcm->mmap_status->hw_ptr, 98873b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent (unsigned int)pcm->mmap_control->appl_ptr, 98973b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent avail); 99073b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent pcm->mmap_control->appl_ptr = 0; 99173b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent return err; 99273b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent } 99373b9c679a656c7b0f5e265dae5a76664c7d03031Eric Laurent continue; 994e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 995e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 996e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 997e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson frames = count; 998e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (frames > avail) 999e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson frames = avail; 1000e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 1001e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (!frames) 1002e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson break; 1003e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 1004e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson /* copy frames from buffer */ 1005e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson frames = pcm_mmap_write_areas(pcm, buffer, offset, frames); 1006e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson if (frames < 0) { 1007e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n", 1008e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (unsigned int)pcm->mmap_status->hw_ptr, 1009e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson (unsigned int)pcm->mmap_control->appl_ptr, 1010e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson avail); 1011e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return frames; 1012e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 1013e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 1014e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson offset += frames; 1015e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson count -= frames; 1016e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson } 1017e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson 1018e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson return 0; 1019e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson} 1020