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(&params);
634edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
635edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                   pcm_format_to_alsa(config->format));
636edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
637edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                   SNDRV_PCM_SUBFORMAT_STD);
638edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
639edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
640edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                  pcm_format_to_bits(config->format));
641edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
642edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                  pcm_format_to_bits(config->format) * config->channels);
643edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
644edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                  config->channels);
645edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
646edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    param_set_int(&params, 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(&params, SNDRV_PCM_HW_PARAM_ACCESS,
661e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson                   SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
662e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson    else
663e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson        param_set_mask(&params, 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, &params)) {
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(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
673e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson    config->period_count = param_get_int(&params, 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