pcm.c revision edff708e3129d7d8e05bbdfb624af97a3264a332
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> 36edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 37edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <sys/ioctl.h> 38edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <sys/mman.h> 39edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <sys/time.h> 40edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 41edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <linux/ioctl.h> 42edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define __force 43edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define __bitwise 44edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define __user 45edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <sound/asound.h> 46edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 47edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <tinyalsa/asoundlib.h> 48edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 49edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL 50edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 51edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic inline int param_is_mask(int p) 52edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 53edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && 54edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); 55edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 56edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 57edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic inline int param_is_interval(int p) 58edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 59edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) && 60edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL); 61edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 62edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 63edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n) 64edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 65edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); 66edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 67edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 68edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) 69edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 70edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); 71edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 72edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 73edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) 74edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 75edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (bit >= SNDRV_MASK_MAX) 76edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return; 77edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (param_is_mask(n)) { 78edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_mask *m = param_to_mask(p, n); 79edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[0] = 0; 80edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[1] = 0; 81edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[bit >> 5] |= (1 << (bit & 31)); 82edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 83edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 84edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 85edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val) 86edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 87edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (param_is_interval(n)) { 88edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_interval *i = param_to_interval(p, n); 89edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->min = val; 90edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 91edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 92edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 93edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned int val) 94edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 95edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (param_is_interval(n)) { 96edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_interval *i = param_to_interval(p, n); 97edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->max = val; 98edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 99edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 100edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 101edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val) 102edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 103edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (param_is_interval(n)) { 104edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_interval *i = param_to_interval(p, n); 105edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->min = val; 106edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->max = val; 107edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->integer = 1; 108edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 109edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 110edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 111edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic void param_init(struct snd_pcm_hw_params *p) 112edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 113edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int n; 114edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 115edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson memset(p, 0, sizeof(*p)); 116edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; 117edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { 118edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_mask *m = param_to_mask(p, n); 119edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[0] = ~0; 120edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson m->bits[1] = ~0; 121edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 122edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; 123edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { 124edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_interval *i = param_to_interval(p, n); 125edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->min = 0; 126edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson i->max = ~0; 127edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 128edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 129edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 130edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define PCM_ERROR_MAX 128 131edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 132edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstruct pcm { 133edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int fd; 134edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson unsigned int flags; 135edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int running:1; 136edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int underruns; 137edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson unsigned int buffer_size; 138edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson char error[PCM_ERROR_MAX]; 139edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct pcm_config config; 140edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson}; 141edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 142edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonunsigned int pcm_get_buffer_size(struct pcm *pcm) 143edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 144edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm->buffer_size; 145edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 146edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 147edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonconst char* pcm_get_error(struct pcm *pcm) 148edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 149edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm->error; 150edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 151edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 152edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic int oops(struct pcm *pcm, int e, const char *fmt, ...) 153edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 154edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson va_list ap; 155edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson int sz; 156edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 157edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson va_start(ap, fmt); 158edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap); 159edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson va_end(ap); 160edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sz = strlen(pcm->error); 161edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 162edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (errno) 163edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson snprintf(pcm->error + sz, PCM_ERROR_MAX - sz, 164edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson ": %s", strerror(e)); 165edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return -1; 166edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 167edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 168edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_write(struct pcm *pcm, void *data, unsigned int count) 169edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 170edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_xferi x; 171edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 172edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm->flags & PCM_IN) 173edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return -EINVAL; 174edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 175edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson x.buf = data; 176edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson x.frames = count / (pcm->config.channels * 2); /* TODO: handle 32bit */ 177edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 178edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (;;) { 179edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!pcm->running) { 180edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) 181edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot prepare channel"); 182edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) 183edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot write initial data"); 184edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 1; 185edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 186edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 187edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { 188edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 0; 189edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (errno == EPIPE) { 190edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson /* we failed to make our window -- try to restart */ 191edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->underruns++; 192edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson continue; 193edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 194edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot write stream data"); 195edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 196edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 197edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 198edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 199edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 200edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_read(struct pcm *pcm, void *data, unsigned int count) 201edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 202edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_xferi x; 203edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 204edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!(pcm->flags & PCM_IN)) 205edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return -EINVAL; 206edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 207edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson x.buf = data; 208edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson x.frames = count / (pcm->config.channels * 2); /* TODO: handle 32bit */ 209edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 210edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson for (;;) { 211edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!pcm->running) { 212edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) 213edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot prepare channel"); 214edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) 215edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot start channel"); 216edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 1; 217edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 218edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { 219edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 0; 220edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (errno == EPIPE) { 221edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson /* we failed to make our window -- try to restart */ 222edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->underruns++; 223edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson continue; 224edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 225edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return oops(pcm, errno, "cannot read stream data"); 226edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 227edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 228edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 229edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 230edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 231edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic struct pcm bad_pcm = { 232edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson .fd = -1, 233edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson}; 234edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 235edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_close(struct pcm *pcm) 236edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 237edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm == &bad_pcm) 238edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 239edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 240edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm->fd >= 0) 241edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson close(pcm->fd); 242edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->running = 0; 243edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->buffer_size = 0; 244edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->fd = -1; 245edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 0; 246edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 247edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 248edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic unsigned int pcm_format_to_alsa(enum pcm_format format) 249edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 250edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson switch (format) { 251edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson case PCM_FORMAT_S32_LE: 252edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return SNDRV_PCM_FORMAT_S32_LE; 253edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson default: 254edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson case PCM_FORMAT_S16_LE: 255edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return SNDRV_PCM_FORMAT_S16_LE; 256edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson }; 257edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 258edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 259edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstatic unsigned int pcm_format_to_bits(enum pcm_format format) 260edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 261edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson switch (format) { 262edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson case PCM_FORMAT_S32_LE: 263edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 32; 264edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson default: 265edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson case PCM_FORMAT_S16_LE: 266edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return 16; 267edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson }; 268edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 269edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 270edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonstruct pcm *pcm_open(unsigned int card, unsigned int device, 271edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson unsigned int flags, struct pcm_config *config) 272edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 273edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct pcm *pcm; 274edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_pcm_info info; 275edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_pcm_hw_params params; 276edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson struct snd_pcm_sw_params sparams; 277edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson char fn[256]; 278edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 279edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm = calloc(1, sizeof(struct pcm)); 280edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (!pcm || !config) 281edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return &bad_pcm; /* TODO: could support default config here */ 282edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 283edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->config = *config; 284edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 285edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device, 286edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson flags & PCM_IN ? 'c' : 'p'); 287edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 288edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->flags = flags; 289edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->fd = open(fn, O_RDWR); 290edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (pcm->fd < 0) { 291edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot open device '%s'", fn); 292edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm; 293edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 294edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 295edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) { 296edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot get info"); 297edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson goto fail; 298edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 299edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 300edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_init(¶ms); 301edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, 302edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson SNDRV_PCM_ACCESS_RW_INTERLEAVED); 303edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, 304edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm_format_to_alsa(config->format)); 305edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT, 306edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson SNDRV_PCM_SUBFORMAT_STD); 307edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size); 308edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 309edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm_format_to_bits(config->format)); 310edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, 311edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm_format_to_bits(config->format) * config->channels); 312edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, 313edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson config->channels); 314edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count); 315edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate); 316edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 317edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { 318edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot set hw params"); 319edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson goto fail; 320edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 321edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 322edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson memset(&sparams, 0, sizeof(sparams)); 323edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.tstamp_mode = SNDRV_PCM_TSTAMP_NONE; 324edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.period_step = 1; 325edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.avail_min = 1; 326edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.start_threshold = config->period_count * config->period_size; 327edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.stop_threshold = config->period_count * config->period_size; 328edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.xfer_align = config->period_size / 2; /* needed for old kernels */ 329edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.silence_size = 0; 330edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson sparams.silence_threshold = 0; 331edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 332edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) { 333edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson oops(pcm, errno, "cannot set sw params"); 334edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson goto fail; 335edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson } 336edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 337edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->buffer_size = config->period_count * config->period_size; 338edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->underruns = 0; 339edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm; 340edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 341edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonfail: 342edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson close(pcm->fd); 343edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson pcm->fd = -1; 344edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm; 345edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 346edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson 347edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint pcm_is_ready(struct pcm *pcm) 348edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{ 349edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson return pcm->fd >= 0; 350edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson} 351