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