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(&params);
301edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
302edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                   SNDRV_PCM_ACCESS_RW_INTERLEAVED);
303edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
304edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                   pcm_format_to_alsa(config->format));
305edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
306edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                   SNDRV_PCM_SUBFORMAT_STD);
307edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
308edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
309edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                  pcm_format_to_bits(config->format));
310edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
311edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                  pcm_format_to_bits(config->format) * config->channels);
312edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
313edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                  config->channels);
314edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
315edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
316edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
317edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
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