1*d0c94b83SXin Li /* pcm.c
2*d0c94b83SXin Li **
3*d0c94b83SXin Li ** Copyright 2011, The Android Open Source Project
4*d0c94b83SXin Li **
5*d0c94b83SXin Li ** Redistribution and use in source and binary forms, with or without
6*d0c94b83SXin Li ** modification, are permitted provided that the following conditions are met:
7*d0c94b83SXin Li ** * Redistributions of source code must retain the above copyright
8*d0c94b83SXin Li ** notice, this list of conditions and the following disclaimer.
9*d0c94b83SXin Li ** * Redistributions in binary form must reproduce the above copyright
10*d0c94b83SXin Li ** notice, this list of conditions and the following disclaimer in the
11*d0c94b83SXin Li ** documentation and/or other materials provided with the distribution.
12*d0c94b83SXin Li ** * Neither the name of The Android Open Source Project nor the names of
13*d0c94b83SXin Li ** its contributors may be used to endorse or promote products derived
14*d0c94b83SXin Li ** from this software without specific prior written permission.
15*d0c94b83SXin Li **
16*d0c94b83SXin Li ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17*d0c94b83SXin Li ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*d0c94b83SXin Li ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*d0c94b83SXin Li ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20*d0c94b83SXin Li ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*d0c94b83SXin Li ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22*d0c94b83SXin Li ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23*d0c94b83SXin Li ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*d0c94b83SXin Li ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*d0c94b83SXin Li ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26*d0c94b83SXin Li ** DAMAGE.
27*d0c94b83SXin Li */
28*d0c94b83SXin Li
29*d0c94b83SXin Li #include <stdbool.h>
30*d0c94b83SXin Li #include <stdio.h>
31*d0c94b83SXin Li #include <stdlib.h>
32*d0c94b83SXin Li #include <fcntl.h>
33*d0c94b83SXin Li #include <stdarg.h>
34*d0c94b83SXin Li #include <string.h>
35*d0c94b83SXin Li #include <errno.h>
36*d0c94b83SXin Li #include <unistd.h>
37*d0c94b83SXin Li #include <poll.h>
38*d0c94b83SXin Li
39*d0c94b83SXin Li #include <sys/ioctl.h>
40*d0c94b83SXin Li #include <sys/mman.h>
41*d0c94b83SXin Li #include <sys/time.h>
42*d0c94b83SXin Li #include <limits.h>
43*d0c94b83SXin Li
44*d0c94b83SXin Li #include <linux/ioctl.h>
45*d0c94b83SXin Li #define __force
46*d0c94b83SXin Li #define __bitwise
47*d0c94b83SXin Li #define __user
48*d0c94b83SXin Li #include <sound/asound.h>
49*d0c94b83SXin Li
50*d0c94b83SXin Li #include <tinyalsa/asoundlib.h>
51*d0c94b83SXin Li #include "pcm_io.h"
52*d0c94b83SXin Li #include "snd_utils.h"
53*d0c94b83SXin Li
54*d0c94b83SXin Li enum {
55*d0c94b83SXin Li PCM_NODE_TYPE_HW = 0,
56*d0c94b83SXin Li PCM_NODE_TYPE_PLUGIN,
57*d0c94b83SXin Li };
58*d0c94b83SXin Li
59*d0c94b83SXin Li #define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
60*d0c94b83SXin Li
61*d0c94b83SXin Li /* Logs information into a string; follows snprintf() in that
62*d0c94b83SXin Li * offset may be greater than size, and though no characters are copied
63*d0c94b83SXin Li * into string, characters are still counted into offset. */
64*d0c94b83SXin Li #define STRLOG(string, offset, size, ...) \
65*d0c94b83SXin Li do { int temp, clipoffset = offset > size ? size : offset; \
66*d0c94b83SXin Li temp = snprintf(string + clipoffset, size - clipoffset, __VA_ARGS__); \
67*d0c94b83SXin Li if (temp > 0) offset += temp; } while (0)
68*d0c94b83SXin Li
69*d0c94b83SXin Li #ifndef ARRAY_SIZE
70*d0c94b83SXin Li #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
71*d0c94b83SXin Li #endif
72*d0c94b83SXin Li
73*d0c94b83SXin Li /* refer to SNDRV_PCM_ACCESS_##index in sound/asound.h. */
74*d0c94b83SXin Li static const char * const access_lookup[] = {
75*d0c94b83SXin Li "MMAP_INTERLEAVED",
76*d0c94b83SXin Li "MMAP_NONINTERLEAVED",
77*d0c94b83SXin Li "MMAP_COMPLEX",
78*d0c94b83SXin Li "RW_INTERLEAVED",
79*d0c94b83SXin Li "RW_NONINTERLEAVED",
80*d0c94b83SXin Li };
81*d0c94b83SXin Li
82*d0c94b83SXin Li /* refer to SNDRV_PCM_FORMAT_##index in sound/asound.h. */
83*d0c94b83SXin Li static const char * const format_lookup[] = {
84*d0c94b83SXin Li /*[0] =*/ "S8",
85*d0c94b83SXin Li "U8",
86*d0c94b83SXin Li "S16_LE",
87*d0c94b83SXin Li "S16_BE",
88*d0c94b83SXin Li "U16_LE",
89*d0c94b83SXin Li "U16_BE",
90*d0c94b83SXin Li "S24_LE",
91*d0c94b83SXin Li "S24_BE",
92*d0c94b83SXin Li "U24_LE",
93*d0c94b83SXin Li "U24_BE",
94*d0c94b83SXin Li "S32_LE",
95*d0c94b83SXin Li "S32_BE",
96*d0c94b83SXin Li "U32_LE",
97*d0c94b83SXin Li "U32_BE",
98*d0c94b83SXin Li "FLOAT_LE",
99*d0c94b83SXin Li "FLOAT_BE",
100*d0c94b83SXin Li "FLOAT64_LE",
101*d0c94b83SXin Li "FLOAT64_BE",
102*d0c94b83SXin Li "IEC958_SUBFRAME_LE",
103*d0c94b83SXin Li "IEC958_SUBFRAME_BE",
104*d0c94b83SXin Li "MU_LAW",
105*d0c94b83SXin Li "A_LAW",
106*d0c94b83SXin Li "IMA_ADPCM",
107*d0c94b83SXin Li "MPEG",
108*d0c94b83SXin Li /*[24] =*/ "GSM",
109*d0c94b83SXin Li /* gap */
110*d0c94b83SXin Li [31] = "SPECIAL",
111*d0c94b83SXin Li "S24_3LE",
112*d0c94b83SXin Li "S24_3BE",
113*d0c94b83SXin Li "U24_3LE",
114*d0c94b83SXin Li "U24_3BE",
115*d0c94b83SXin Li "S20_3LE",
116*d0c94b83SXin Li "S20_3BE",
117*d0c94b83SXin Li "U20_3LE",
118*d0c94b83SXin Li "U20_3BE",
119*d0c94b83SXin Li "S18_3LE",
120*d0c94b83SXin Li "S18_3BE",
121*d0c94b83SXin Li "U18_3LE",
122*d0c94b83SXin Li /*[43] =*/ "U18_3BE",
123*d0c94b83SXin Li #if 0
124*d0c94b83SXin Li /* recent additions, may not be present on local asound.h */
125*d0c94b83SXin Li "G723_24",
126*d0c94b83SXin Li "G723_24_1B",
127*d0c94b83SXin Li "G723_40",
128*d0c94b83SXin Li "G723_40_1B",
129*d0c94b83SXin Li "DSD_U8",
130*d0c94b83SXin Li "DSD_U16_LE",
131*d0c94b83SXin Li #endif
132*d0c94b83SXin Li };
133*d0c94b83SXin Li
134*d0c94b83SXin Li extern struct pcm_ops hw_ops;
135*d0c94b83SXin Li extern struct pcm_ops plug_ops;
136*d0c94b83SXin Li
137*d0c94b83SXin Li /* refer to SNDRV_PCM_SUBFORMAT_##index in sound/asound.h. */
138*d0c94b83SXin Li static const char * const subformat_lookup[] = {
139*d0c94b83SXin Li "STD",
140*d0c94b83SXin Li };
141*d0c94b83SXin Li
param_is_mask(int p)142*d0c94b83SXin Li static inline int param_is_mask(int p)
143*d0c94b83SXin Li {
144*d0c94b83SXin Li return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
145*d0c94b83SXin Li (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
146*d0c94b83SXin Li }
147*d0c94b83SXin Li
param_is_interval(int p)148*d0c94b83SXin Li static inline int param_is_interval(int p)
149*d0c94b83SXin Li {
150*d0c94b83SXin Li return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
151*d0c94b83SXin Li (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
152*d0c94b83SXin Li }
153*d0c94b83SXin Li
param_to_interval(struct snd_pcm_hw_params * p,int n)154*d0c94b83SXin Li static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
155*d0c94b83SXin Li {
156*d0c94b83SXin Li return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
157*d0c94b83SXin Li }
158*d0c94b83SXin Li
param_to_mask(struct snd_pcm_hw_params * p,int n)159*d0c94b83SXin Li static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
160*d0c94b83SXin Li {
161*d0c94b83SXin Li return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
162*d0c94b83SXin Li }
163*d0c94b83SXin Li
param_set_mask(struct snd_pcm_hw_params * p,int n,unsigned int bit)164*d0c94b83SXin Li static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
165*d0c94b83SXin Li {
166*d0c94b83SXin Li if (bit >= SNDRV_MASK_MAX)
167*d0c94b83SXin Li return;
168*d0c94b83SXin Li if (param_is_mask(n)) {
169*d0c94b83SXin Li struct snd_mask *m = param_to_mask(p, n);
170*d0c94b83SXin Li m->bits[0] = 0;
171*d0c94b83SXin Li m->bits[1] = 0;
172*d0c94b83SXin Li m->bits[bit >> 5] |= (1 << (bit & 31));
173*d0c94b83SXin Li }
174*d0c94b83SXin Li }
175*d0c94b83SXin Li
param_set_min(struct snd_pcm_hw_params * p,int n,unsigned int val)176*d0c94b83SXin Li static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
177*d0c94b83SXin Li {
178*d0c94b83SXin Li if (param_is_interval(n)) {
179*d0c94b83SXin Li struct snd_interval *i = param_to_interval(p, n);
180*d0c94b83SXin Li i->min = val;
181*d0c94b83SXin Li }
182*d0c94b83SXin Li }
183*d0c94b83SXin Li
param_get_min(struct snd_pcm_hw_params * p,int n)184*d0c94b83SXin Li static unsigned int param_get_min(struct snd_pcm_hw_params *p, int n)
185*d0c94b83SXin Li {
186*d0c94b83SXin Li if (param_is_interval(n)) {
187*d0c94b83SXin Li struct snd_interval *i = param_to_interval(p, n);
188*d0c94b83SXin Li return i->min;
189*d0c94b83SXin Li }
190*d0c94b83SXin Li return 0;
191*d0c94b83SXin Li }
192*d0c94b83SXin Li
param_set_max(struct snd_pcm_hw_params * p,int n,unsigned int val)193*d0c94b83SXin Li static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned int val)
194*d0c94b83SXin Li {
195*d0c94b83SXin Li if (param_is_interval(n)) {
196*d0c94b83SXin Li struct snd_interval *i = param_to_interval(p, n);
197*d0c94b83SXin Li i->max = val;
198*d0c94b83SXin Li }
199*d0c94b83SXin Li }
200*d0c94b83SXin Li
param_get_max(struct snd_pcm_hw_params * p,int n)201*d0c94b83SXin Li static unsigned int param_get_max(struct snd_pcm_hw_params *p, int n)
202*d0c94b83SXin Li {
203*d0c94b83SXin Li if (param_is_interval(n)) {
204*d0c94b83SXin Li struct snd_interval *i = param_to_interval(p, n);
205*d0c94b83SXin Li return i->max;
206*d0c94b83SXin Li }
207*d0c94b83SXin Li return 0;
208*d0c94b83SXin Li }
209*d0c94b83SXin Li
param_set_int(struct snd_pcm_hw_params * p,int n,unsigned int val)210*d0c94b83SXin Li static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
211*d0c94b83SXin Li {
212*d0c94b83SXin Li if (param_is_interval(n)) {
213*d0c94b83SXin Li struct snd_interval *i = param_to_interval(p, n);
214*d0c94b83SXin Li i->min = val;
215*d0c94b83SXin Li i->max = val;
216*d0c94b83SXin Li i->integer = 1;
217*d0c94b83SXin Li }
218*d0c94b83SXin Li }
219*d0c94b83SXin Li
param_get_int(struct snd_pcm_hw_params * p,int n)220*d0c94b83SXin Li static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n)
221*d0c94b83SXin Li {
222*d0c94b83SXin Li if (param_is_interval(n)) {
223*d0c94b83SXin Li struct snd_interval *i = param_to_interval(p, n);
224*d0c94b83SXin Li if (i->integer)
225*d0c94b83SXin Li return i->max;
226*d0c94b83SXin Li }
227*d0c94b83SXin Li return 0;
228*d0c94b83SXin Li }
229*d0c94b83SXin Li
param_init(struct snd_pcm_hw_params * p)230*d0c94b83SXin Li static void param_init(struct snd_pcm_hw_params *p)
231*d0c94b83SXin Li {
232*d0c94b83SXin Li int n;
233*d0c94b83SXin Li
234*d0c94b83SXin Li memset(p, 0, sizeof(*p));
235*d0c94b83SXin Li for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
236*d0c94b83SXin Li n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
237*d0c94b83SXin Li struct snd_mask *m = param_to_mask(p, n);
238*d0c94b83SXin Li m->bits[0] = ~0;
239*d0c94b83SXin Li m->bits[1] = ~0;
240*d0c94b83SXin Li }
241*d0c94b83SXin Li for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
242*d0c94b83SXin Li n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
243*d0c94b83SXin Li struct snd_interval *i = param_to_interval(p, n);
244*d0c94b83SXin Li i->min = 0;
245*d0c94b83SXin Li i->max = ~0;
246*d0c94b83SXin Li }
247*d0c94b83SXin Li p->rmask = ~0U;
248*d0c94b83SXin Li p->cmask = 0;
249*d0c94b83SXin Li p->info = ~0U;
250*d0c94b83SXin Li }
251*d0c94b83SXin Li
252*d0c94b83SXin Li #define PCM_ERROR_MAX 128
253*d0c94b83SXin Li
254*d0c94b83SXin Li struct pcm {
255*d0c94b83SXin Li int fd;
256*d0c94b83SXin Li unsigned int flags;
257*d0c94b83SXin Li bool running:1;
258*d0c94b83SXin Li bool prepared:1;
259*d0c94b83SXin Li int xruns;
260*d0c94b83SXin Li unsigned int buffer_size;
261*d0c94b83SXin Li unsigned long boundary;
262*d0c94b83SXin Li char error[PCM_ERROR_MAX];
263*d0c94b83SXin Li struct pcm_config config;
264*d0c94b83SXin Li struct snd_pcm_mmap_status *mmap_status;
265*d0c94b83SXin Li struct snd_pcm_mmap_control *mmap_control;
266*d0c94b83SXin Li struct snd_pcm_sync_ptr *sync_ptr;
267*d0c94b83SXin Li void *mmap_buffer;
268*d0c94b83SXin Li unsigned int noirq_frames_per_msec;
269*d0c94b83SXin Li int wait_for_avail_min;
270*d0c94b83SXin Li unsigned int subdevice;
271*d0c94b83SXin Li
272*d0c94b83SXin Li struct pcm_ops *ops;
273*d0c94b83SXin Li void *data;
274*d0c94b83SXin Li void *snd_node;
275*d0c94b83SXin Li };
276*d0c94b83SXin Li
pcm_get_buffer_size(struct pcm * pcm)277*d0c94b83SXin Li unsigned int pcm_get_buffer_size(struct pcm *pcm)
278*d0c94b83SXin Li {
279*d0c94b83SXin Li return pcm->buffer_size;
280*d0c94b83SXin Li }
281*d0c94b83SXin Li
pcm_get_error(struct pcm * pcm)282*d0c94b83SXin Li const char* pcm_get_error(struct pcm *pcm)
283*d0c94b83SXin Li {
284*d0c94b83SXin Li return pcm->error;
285*d0c94b83SXin Li }
286*d0c94b83SXin Li
pcm_get_subdevice(struct pcm * pcm)287*d0c94b83SXin Li unsigned int pcm_get_subdevice(struct pcm *pcm)
288*d0c94b83SXin Li {
289*d0c94b83SXin Li return pcm->subdevice;
290*d0c94b83SXin Li }
291*d0c94b83SXin Li
oops(struct pcm * pcm,int e,const char * fmt,...)292*d0c94b83SXin Li static int oops(struct pcm *pcm, int e, const char *fmt, ...)
293*d0c94b83SXin Li {
294*d0c94b83SXin Li va_list ap;
295*d0c94b83SXin Li int sz;
296*d0c94b83SXin Li
297*d0c94b83SXin Li va_start(ap, fmt);
298*d0c94b83SXin Li vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
299*d0c94b83SXin Li va_end(ap);
300*d0c94b83SXin Li sz = strlen(pcm->error);
301*d0c94b83SXin Li
302*d0c94b83SXin Li if (e)
303*d0c94b83SXin Li snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
304*d0c94b83SXin Li ": %s", strerror(e));
305*d0c94b83SXin Li return -1;
306*d0c94b83SXin Li }
307*d0c94b83SXin Li
pcm_format_to_alsa(enum pcm_format format)308*d0c94b83SXin Li static unsigned int pcm_format_to_alsa(enum pcm_format format)
309*d0c94b83SXin Li {
310*d0c94b83SXin Li switch (format) {
311*d0c94b83SXin Li case PCM_FORMAT_S32_LE:
312*d0c94b83SXin Li return SNDRV_PCM_FORMAT_S32_LE;
313*d0c94b83SXin Li case PCM_FORMAT_S8:
314*d0c94b83SXin Li return SNDRV_PCM_FORMAT_S8;
315*d0c94b83SXin Li case PCM_FORMAT_S24_3LE:
316*d0c94b83SXin Li return SNDRV_PCM_FORMAT_S24_3LE;
317*d0c94b83SXin Li case PCM_FORMAT_S24_LE:
318*d0c94b83SXin Li return SNDRV_PCM_FORMAT_S24_LE;
319*d0c94b83SXin Li default:
320*d0c94b83SXin Li case PCM_FORMAT_S16_LE:
321*d0c94b83SXin Li return SNDRV_PCM_FORMAT_S16_LE;
322*d0c94b83SXin Li };
323*d0c94b83SXin Li }
324*d0c94b83SXin Li
pcm_format_to_bits(enum pcm_format format)325*d0c94b83SXin Li unsigned int pcm_format_to_bits(enum pcm_format format)
326*d0c94b83SXin Li {
327*d0c94b83SXin Li switch (format) {
328*d0c94b83SXin Li case PCM_FORMAT_S32_LE:
329*d0c94b83SXin Li case PCM_FORMAT_S24_LE:
330*d0c94b83SXin Li return 32;
331*d0c94b83SXin Li case PCM_FORMAT_S24_3LE:
332*d0c94b83SXin Li return 24;
333*d0c94b83SXin Li default:
334*d0c94b83SXin Li case PCM_FORMAT_S16_LE:
335*d0c94b83SXin Li return 16;
336*d0c94b83SXin Li };
337*d0c94b83SXin Li }
338*d0c94b83SXin Li
pcm_bytes_to_frames(struct pcm * pcm,unsigned int bytes)339*d0c94b83SXin Li unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes)
340*d0c94b83SXin Li {
341*d0c94b83SXin Li return bytes / (pcm->config.channels *
342*d0c94b83SXin Li (pcm_format_to_bits(pcm->config.format) >> 3));
343*d0c94b83SXin Li }
344*d0c94b83SXin Li
pcm_frames_to_bytes(struct pcm * pcm,unsigned int frames)345*d0c94b83SXin Li unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames)
346*d0c94b83SXin Li {
347*d0c94b83SXin Li return frames * pcm->config.channels *
348*d0c94b83SXin Li (pcm_format_to_bits(pcm->config.format) >> 3);
349*d0c94b83SXin Li }
350*d0c94b83SXin Li
pcm_sync_ptr(struct pcm * pcm,int flags)351*d0c94b83SXin Li static int pcm_sync_ptr(struct pcm *pcm, int flags) {
352*d0c94b83SXin Li if (pcm->sync_ptr) {
353*d0c94b83SXin Li pcm->sync_ptr->flags = flags;
354*d0c94b83SXin Li if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_SYNC_PTR,
355*d0c94b83SXin Li pcm->sync_ptr) < 0)
356*d0c94b83SXin Li return -1;
357*d0c94b83SXin Li }
358*d0c94b83SXin Li return 0;
359*d0c94b83SXin Li }
360*d0c94b83SXin Li
pcm_hw_mmap_status(struct pcm * pcm)361*d0c94b83SXin Li static int pcm_hw_mmap_status(struct pcm *pcm) {
362*d0c94b83SXin Li
363*d0c94b83SXin Li if (pcm->sync_ptr)
364*d0c94b83SXin Li return 0;
365*d0c94b83SXin Li
366*d0c94b83SXin Li int page_size = sysconf(_SC_PAGE_SIZE);
367*d0c94b83SXin Li pcm->mmap_status = pcm->ops->mmap(pcm->data, NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
368*d0c94b83SXin Li SNDRV_PCM_MMAP_OFFSET_STATUS);
369*d0c94b83SXin Li if (pcm->mmap_status == MAP_FAILED)
370*d0c94b83SXin Li pcm->mmap_status = NULL;
371*d0c94b83SXin Li if (!pcm->mmap_status)
372*d0c94b83SXin Li goto mmap_error;
373*d0c94b83SXin Li
374*d0c94b83SXin Li pcm->mmap_control = pcm->ops->mmap(pcm->data, NULL, page_size, PROT_READ | PROT_WRITE,
375*d0c94b83SXin Li MAP_FILE | MAP_SHARED, SNDRV_PCM_MMAP_OFFSET_CONTROL);
376*d0c94b83SXin Li if (pcm->mmap_control == MAP_FAILED)
377*d0c94b83SXin Li pcm->mmap_control = NULL;
378*d0c94b83SXin Li if (!pcm->mmap_control) {
379*d0c94b83SXin Li pcm->ops->munmap(pcm->data, pcm->mmap_status, page_size);
380*d0c94b83SXin Li pcm->mmap_status = NULL;
381*d0c94b83SXin Li goto mmap_error;
382*d0c94b83SXin Li }
383*d0c94b83SXin Li if (pcm->flags & PCM_MMAP)
384*d0c94b83SXin Li pcm->mmap_control->avail_min = pcm->config.avail_min;
385*d0c94b83SXin Li else
386*d0c94b83SXin Li pcm->mmap_control->avail_min = 1;
387*d0c94b83SXin Li
388*d0c94b83SXin Li return 0;
389*d0c94b83SXin Li
390*d0c94b83SXin Li mmap_error:
391*d0c94b83SXin Li
392*d0c94b83SXin Li pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr));
393*d0c94b83SXin Li if (!pcm->sync_ptr)
394*d0c94b83SXin Li return -ENOMEM;
395*d0c94b83SXin Li pcm->mmap_status = &pcm->sync_ptr->s.status;
396*d0c94b83SXin Li pcm->mmap_control = &pcm->sync_ptr->c.control;
397*d0c94b83SXin Li if (pcm->flags & PCM_MMAP)
398*d0c94b83SXin Li pcm->mmap_control->avail_min = pcm->config.avail_min;
399*d0c94b83SXin Li else
400*d0c94b83SXin Li pcm->mmap_control->avail_min = 1;
401*d0c94b83SXin Li
402*d0c94b83SXin Li pcm_sync_ptr(pcm, 0);
403*d0c94b83SXin Li
404*d0c94b83SXin Li return 0;
405*d0c94b83SXin Li }
406*d0c94b83SXin Li
pcm_hw_munmap_status(struct pcm * pcm)407*d0c94b83SXin Li static void pcm_hw_munmap_status(struct pcm *pcm) {
408*d0c94b83SXin Li if (pcm->sync_ptr) {
409*d0c94b83SXin Li free(pcm->sync_ptr);
410*d0c94b83SXin Li pcm->sync_ptr = NULL;
411*d0c94b83SXin Li } else {
412*d0c94b83SXin Li int page_size = sysconf(_SC_PAGE_SIZE);
413*d0c94b83SXin Li if (pcm->mmap_status)
414*d0c94b83SXin Li pcm->ops->munmap(pcm->data, pcm->mmap_status, page_size);
415*d0c94b83SXin Li if (pcm->mmap_control)
416*d0c94b83SXin Li pcm->ops->munmap(pcm->data, pcm->mmap_control, page_size);
417*d0c94b83SXin Li }
418*d0c94b83SXin Li pcm->mmap_status = NULL;
419*d0c94b83SXin Li pcm->mmap_control = NULL;
420*d0c94b83SXin Li }
421*d0c94b83SXin Li
pcm_areas_copy(struct pcm * pcm,unsigned int pcm_offset,char * buf,unsigned int src_offset,unsigned int frames)422*d0c94b83SXin Li static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
423*d0c94b83SXin Li char *buf, unsigned int src_offset,
424*d0c94b83SXin Li unsigned int frames)
425*d0c94b83SXin Li {
426*d0c94b83SXin Li int size_bytes = pcm_frames_to_bytes(pcm, frames);
427*d0c94b83SXin Li int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset);
428*d0c94b83SXin Li int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
429*d0c94b83SXin Li
430*d0c94b83SXin Li /* interleaved only atm */
431*d0c94b83SXin Li if (pcm->flags & PCM_IN)
432*d0c94b83SXin Li memcpy(buf + src_offset_bytes,
433*d0c94b83SXin Li (char*)pcm->mmap_buffer + pcm_offset_bytes,
434*d0c94b83SXin Li size_bytes);
435*d0c94b83SXin Li else
436*d0c94b83SXin Li memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
437*d0c94b83SXin Li buf + src_offset_bytes,
438*d0c94b83SXin Li size_bytes);
439*d0c94b83SXin Li return 0;
440*d0c94b83SXin Li }
441*d0c94b83SXin Li
pcm_mmap_transfer_areas(struct pcm * pcm,char * buf,unsigned int offset,unsigned int size)442*d0c94b83SXin Li static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf,
443*d0c94b83SXin Li unsigned int offset, unsigned int size)
444*d0c94b83SXin Li {
445*d0c94b83SXin Li void *pcm_areas;
446*d0c94b83SXin Li int commit;
447*d0c94b83SXin Li unsigned int pcm_offset, frames, count = 0;
448*d0c94b83SXin Li
449*d0c94b83SXin Li while (size > 0) {
450*d0c94b83SXin Li frames = size;
451*d0c94b83SXin Li pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
452*d0c94b83SXin Li pcm_areas_copy(pcm, pcm_offset, buf, offset, frames);
453*d0c94b83SXin Li commit = pcm_mmap_commit(pcm, pcm_offset, frames);
454*d0c94b83SXin Li if (commit < 0) {
455*d0c94b83SXin Li oops(pcm, errno, "failed to commit %d frames\n", frames);
456*d0c94b83SXin Li return commit;
457*d0c94b83SXin Li }
458*d0c94b83SXin Li
459*d0c94b83SXin Li offset += commit;
460*d0c94b83SXin Li count += commit;
461*d0c94b83SXin Li size -= commit;
462*d0c94b83SXin Li }
463*d0c94b83SXin Li return count;
464*d0c94b83SXin Li }
465*d0c94b83SXin Li
pcm_get_htimestamp(struct pcm * pcm,unsigned int * avail,struct timespec * tstamp)466*d0c94b83SXin Li int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
467*d0c94b83SXin Li struct timespec *tstamp)
468*d0c94b83SXin Li {
469*d0c94b83SXin Li snd_pcm_sframes_t frames;
470*d0c94b83SXin Li int rc;
471*d0c94b83SXin Li snd_pcm_uframes_t hw_ptr;
472*d0c94b83SXin Li
473*d0c94b83SXin Li if (!pcm_is_ready(pcm))
474*d0c94b83SXin Li return -1;
475*d0c94b83SXin Li
476*d0c94b83SXin Li rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
477*d0c94b83SXin Li if (rc < 0)
478*d0c94b83SXin Li return -1;
479*d0c94b83SXin Li
480*d0c94b83SXin Li if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
481*d0c94b83SXin Li (pcm->mmap_status->state != PCM_STATE_DRAINING))
482*d0c94b83SXin Li return -1;
483*d0c94b83SXin Li
484*d0c94b83SXin Li *tstamp = pcm->mmap_status->tstamp;
485*d0c94b83SXin Li if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
486*d0c94b83SXin Li return -1;
487*d0c94b83SXin Li
488*d0c94b83SXin Li hw_ptr = pcm->mmap_status->hw_ptr;
489*d0c94b83SXin Li if (pcm->flags & PCM_IN)
490*d0c94b83SXin Li frames = hw_ptr - pcm->mmap_control->appl_ptr;
491*d0c94b83SXin Li else
492*d0c94b83SXin Li frames = hw_ptr + (snd_pcm_uframes_t) pcm->buffer_size -
493*d0c94b83SXin Li pcm->mmap_control->appl_ptr;
494*d0c94b83SXin Li
495*d0c94b83SXin Li if (frames < 0)
496*d0c94b83SXin Li frames += pcm->boundary;
497*d0c94b83SXin Li else if (frames >= (snd_pcm_sframes_t) pcm->boundary)
498*d0c94b83SXin Li frames -= pcm->boundary;
499*d0c94b83SXin Li
500*d0c94b83SXin Li *avail = (unsigned int)frames;
501*d0c94b83SXin Li
502*d0c94b83SXin Li return 0;
503*d0c94b83SXin Li }
504*d0c94b83SXin Li
pcm_mmap_get_hw_ptr(struct pcm * pcm,unsigned int * hw_ptr,struct timespec * tstamp)505*d0c94b83SXin Li int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp)
506*d0c94b83SXin Li {
507*d0c94b83SXin Li int frames;
508*d0c94b83SXin Li int rc;
509*d0c94b83SXin Li
510*d0c94b83SXin Li if (pcm == NULL || hw_ptr == NULL || tstamp == NULL)
511*d0c94b83SXin Li return oops(pcm, EINVAL, "pcm %p, hw_ptr %p, tstamp %p", pcm, hw_ptr, tstamp);
512*d0c94b83SXin Li
513*d0c94b83SXin Li if (!pcm_is_ready(pcm))
514*d0c94b83SXin Li return oops(pcm, errno, "pcm_is_ready failed");
515*d0c94b83SXin Li
516*d0c94b83SXin Li rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
517*d0c94b83SXin Li if (rc < 0)
518*d0c94b83SXin Li return oops(pcm, errno, "pcm_sync_ptr failed");
519*d0c94b83SXin Li
520*d0c94b83SXin Li if (pcm->mmap_status == NULL)
521*d0c94b83SXin Li return oops(pcm, EINVAL, "pcm %p, mmap_status is NULL", pcm);
522*d0c94b83SXin Li
523*d0c94b83SXin Li if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
524*d0c94b83SXin Li (pcm->mmap_status->state != PCM_STATE_DRAINING))
525*d0c94b83SXin Li return oops(pcm, ENOSYS, "invalid stream state %d", pcm->mmap_status->state);
526*d0c94b83SXin Li
527*d0c94b83SXin Li *tstamp = pcm->mmap_status->tstamp;
528*d0c94b83SXin Li if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
529*d0c94b83SXin Li return oops(pcm, errno, "invalid time stamp");
530*d0c94b83SXin Li
531*d0c94b83SXin Li *hw_ptr = pcm->mmap_status->hw_ptr;
532*d0c94b83SXin Li
533*d0c94b83SXin Li return 0;
534*d0c94b83SXin Li }
535*d0c94b83SXin Li
pcm_write(struct pcm * pcm,const void * data,unsigned int count)536*d0c94b83SXin Li int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
537*d0c94b83SXin Li {
538*d0c94b83SXin Li struct snd_xferi x;
539*d0c94b83SXin Li
540*d0c94b83SXin Li if (pcm->flags & PCM_IN)
541*d0c94b83SXin Li return -EINVAL;
542*d0c94b83SXin Li
543*d0c94b83SXin Li x.buf = (void*)data;
544*d0c94b83SXin Li x.frames = count / (pcm->config.channels *
545*d0c94b83SXin Li pcm_format_to_bits(pcm->config.format) / 8);
546*d0c94b83SXin Li
547*d0c94b83SXin Li for (;;) {
548*d0c94b83SXin Li if (!pcm->running) {
549*d0c94b83SXin Li int prepare_error = pcm_prepare(pcm);
550*d0c94b83SXin Li if (prepare_error)
551*d0c94b83SXin Li return prepare_error;
552*d0c94b83SXin Li if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
553*d0c94b83SXin Li return oops(pcm, errno, "cannot write initial data");
554*d0c94b83SXin Li pcm->running = true;
555*d0c94b83SXin Li return 0;
556*d0c94b83SXin Li }
557*d0c94b83SXin Li if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
558*d0c94b83SXin Li pcm->prepared = false;
559*d0c94b83SXin Li pcm->running = false;
560*d0c94b83SXin Li if (errno == EPIPE) {
561*d0c94b83SXin Li /* we failed to make our window -- try to restart if we are
562*d0c94b83SXin Li * allowed to do so. Otherwise, simply allow the EPIPE error to
563*d0c94b83SXin Li * propagate up to the app level */
564*d0c94b83SXin Li pcm->xruns++;
565*d0c94b83SXin Li if (pcm->flags & PCM_NORESTART)
566*d0c94b83SXin Li return -EPIPE;
567*d0c94b83SXin Li continue;
568*d0c94b83SXin Li }
569*d0c94b83SXin Li return oops(pcm, errno, "cannot write stream data");
570*d0c94b83SXin Li }
571*d0c94b83SXin Li return 0;
572*d0c94b83SXin Li }
573*d0c94b83SXin Li }
574*d0c94b83SXin Li
pcm_read(struct pcm * pcm,void * data,unsigned int count)575*d0c94b83SXin Li int pcm_read(struct pcm *pcm, void *data, unsigned int count)
576*d0c94b83SXin Li {
577*d0c94b83SXin Li struct snd_xferi x;
578*d0c94b83SXin Li
579*d0c94b83SXin Li if (!(pcm->flags & PCM_IN))
580*d0c94b83SXin Li return -EINVAL;
581*d0c94b83SXin Li
582*d0c94b83SXin Li x.buf = data;
583*d0c94b83SXin Li x.frames = count / (pcm->config.channels *
584*d0c94b83SXin Li pcm_format_to_bits(pcm->config.format) / 8);
585*d0c94b83SXin Li
586*d0c94b83SXin Li for (;;) {
587*d0c94b83SXin Li if (!pcm->running) {
588*d0c94b83SXin Li if (pcm_start(pcm) < 0) {
589*d0c94b83SXin Li fprintf(stderr, "start error");
590*d0c94b83SXin Li return -errno;
591*d0c94b83SXin Li }
592*d0c94b83SXin Li }
593*d0c94b83SXin Li if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
594*d0c94b83SXin Li pcm->prepared = false;
595*d0c94b83SXin Li pcm->running = false;
596*d0c94b83SXin Li if (errno == EPIPE) {
597*d0c94b83SXin Li /* we failed to make our window -- try to restart */
598*d0c94b83SXin Li pcm->xruns++;
599*d0c94b83SXin Li continue;
600*d0c94b83SXin Li }
601*d0c94b83SXin Li return oops(pcm, errno, "cannot read stream data");
602*d0c94b83SXin Li }
603*d0c94b83SXin Li return 0;
604*d0c94b83SXin Li }
605*d0c94b83SXin Li }
606*d0c94b83SXin Li
607*d0c94b83SXin Li static struct pcm bad_pcm = {
608*d0c94b83SXin Li .fd = -1,
609*d0c94b83SXin Li };
610*d0c94b83SXin Li
pcm_params_get(unsigned int card,unsigned int device,unsigned int flags)611*d0c94b83SXin Li struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
612*d0c94b83SXin Li unsigned int flags)
613*d0c94b83SXin Li {
614*d0c94b83SXin Li struct snd_pcm_hw_params *params;
615*d0c94b83SXin Li enum snd_node_type pcm_type;
616*d0c94b83SXin Li struct pcm_ops *ops;
617*d0c94b83SXin Li void *snd_node, *data;
618*d0c94b83SXin Li int fd;
619*d0c94b83SXin Li
620*d0c94b83SXin Li snd_node = snd_utils_get_dev_node(card, device, NODE_PCM);
621*d0c94b83SXin Li pcm_type = snd_utils_get_node_type(snd_node);
622*d0c94b83SXin Li if (pcm_type == SND_NODE_TYPE_PLUGIN)
623*d0c94b83SXin Li ops = &plug_ops;
624*d0c94b83SXin Li else
625*d0c94b83SXin Li ops = &hw_ops;
626*d0c94b83SXin Li
627*d0c94b83SXin Li fd = ops->open(card, device, flags, &data, snd_node);
628*d0c94b83SXin Li if (fd < 0) {
629*d0c94b83SXin Li fprintf(stderr, "cannot open device %u for card %u\n",
630*d0c94b83SXin Li device, card);
631*d0c94b83SXin Li goto err_open;
632*d0c94b83SXin Li }
633*d0c94b83SXin Li
634*d0c94b83SXin Li params = calloc(1, sizeof(struct snd_pcm_hw_params));
635*d0c94b83SXin Li if (!params)
636*d0c94b83SXin Li goto err_calloc;
637*d0c94b83SXin Li
638*d0c94b83SXin Li param_init(params);
639*d0c94b83SXin Li if (ops->ioctl(data, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
640*d0c94b83SXin Li fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
641*d0c94b83SXin Li goto err_hw_refine;
642*d0c94b83SXin Li }
643*d0c94b83SXin Li
644*d0c94b83SXin Li snd_utils_put_dev_node(snd_node);
645*d0c94b83SXin Li ops->close(data);
646*d0c94b83SXin Li
647*d0c94b83SXin Li return (struct pcm_params *)params;
648*d0c94b83SXin Li
649*d0c94b83SXin Li err_hw_refine:
650*d0c94b83SXin Li free(params);
651*d0c94b83SXin Li err_calloc:
652*d0c94b83SXin Li ops->close(data);
653*d0c94b83SXin Li err_open:
654*d0c94b83SXin Li snd_utils_put_dev_node(snd_node);
655*d0c94b83SXin Li return NULL;
656*d0c94b83SXin Li }
657*d0c94b83SXin Li
pcm_params_free(struct pcm_params * pcm_params)658*d0c94b83SXin Li void pcm_params_free(struct pcm_params *pcm_params)
659*d0c94b83SXin Li {
660*d0c94b83SXin Li struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
661*d0c94b83SXin Li
662*d0c94b83SXin Li if (params)
663*d0c94b83SXin Li free(params);
664*d0c94b83SXin Li }
665*d0c94b83SXin Li
pcm_param_to_alsa(enum pcm_param param)666*d0c94b83SXin Li static int pcm_param_to_alsa(enum pcm_param param)
667*d0c94b83SXin Li {
668*d0c94b83SXin Li switch (param) {
669*d0c94b83SXin Li case PCM_PARAM_ACCESS:
670*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_ACCESS;
671*d0c94b83SXin Li case PCM_PARAM_FORMAT:
672*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_FORMAT;
673*d0c94b83SXin Li case PCM_PARAM_SUBFORMAT:
674*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_SUBFORMAT;
675*d0c94b83SXin Li case PCM_PARAM_SAMPLE_BITS:
676*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_SAMPLE_BITS;
677*d0c94b83SXin Li break;
678*d0c94b83SXin Li case PCM_PARAM_FRAME_BITS:
679*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_FRAME_BITS;
680*d0c94b83SXin Li break;
681*d0c94b83SXin Li case PCM_PARAM_CHANNELS:
682*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_CHANNELS;
683*d0c94b83SXin Li break;
684*d0c94b83SXin Li case PCM_PARAM_RATE:
685*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_RATE;
686*d0c94b83SXin Li break;
687*d0c94b83SXin Li case PCM_PARAM_PERIOD_TIME:
688*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_PERIOD_TIME;
689*d0c94b83SXin Li break;
690*d0c94b83SXin Li case PCM_PARAM_PERIOD_SIZE:
691*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_PERIOD_SIZE;
692*d0c94b83SXin Li break;
693*d0c94b83SXin Li case PCM_PARAM_PERIOD_BYTES:
694*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_PERIOD_BYTES;
695*d0c94b83SXin Li break;
696*d0c94b83SXin Li case PCM_PARAM_PERIODS:
697*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_PERIODS;
698*d0c94b83SXin Li break;
699*d0c94b83SXin Li case PCM_PARAM_BUFFER_TIME:
700*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_BUFFER_TIME;
701*d0c94b83SXin Li break;
702*d0c94b83SXin Li case PCM_PARAM_BUFFER_SIZE:
703*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_BUFFER_SIZE;
704*d0c94b83SXin Li break;
705*d0c94b83SXin Li case PCM_PARAM_BUFFER_BYTES:
706*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_BUFFER_BYTES;
707*d0c94b83SXin Li break;
708*d0c94b83SXin Li case PCM_PARAM_TICK_TIME:
709*d0c94b83SXin Li return SNDRV_PCM_HW_PARAM_TICK_TIME;
710*d0c94b83SXin Li break;
711*d0c94b83SXin Li
712*d0c94b83SXin Li default:
713*d0c94b83SXin Li return -1;
714*d0c94b83SXin Li }
715*d0c94b83SXin Li }
716*d0c94b83SXin Li
pcm_params_get_mask(const struct pcm_params * pcm_params,enum pcm_param param)717*d0c94b83SXin Li struct pcm_mask *pcm_params_get_mask(const struct pcm_params *pcm_params,
718*d0c94b83SXin Li enum pcm_param param)
719*d0c94b83SXin Li {
720*d0c94b83SXin Li int p;
721*d0c94b83SXin Li struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
722*d0c94b83SXin Li if (params == NULL) {
723*d0c94b83SXin Li return NULL;
724*d0c94b83SXin Li }
725*d0c94b83SXin Li
726*d0c94b83SXin Li p = pcm_param_to_alsa(param);
727*d0c94b83SXin Li if (p < 0 || !param_is_mask(p)) {
728*d0c94b83SXin Li return NULL;
729*d0c94b83SXin Li }
730*d0c94b83SXin Li
731*d0c94b83SXin Li return (struct pcm_mask *)param_to_mask(params, p);
732*d0c94b83SXin Li }
733*d0c94b83SXin Li
pcm_params_get_min(const struct pcm_params * pcm_params,enum pcm_param param)734*d0c94b83SXin Li unsigned int pcm_params_get_min(const struct pcm_params *pcm_params,
735*d0c94b83SXin Li enum pcm_param param)
736*d0c94b83SXin Li {
737*d0c94b83SXin Li struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
738*d0c94b83SXin Li int p;
739*d0c94b83SXin Li
740*d0c94b83SXin Li if (!params)
741*d0c94b83SXin Li return 0;
742*d0c94b83SXin Li
743*d0c94b83SXin Li p = pcm_param_to_alsa(param);
744*d0c94b83SXin Li if (p < 0)
745*d0c94b83SXin Li return 0;
746*d0c94b83SXin Li
747*d0c94b83SXin Li return param_get_min(params, p);
748*d0c94b83SXin Li }
749*d0c94b83SXin Li
pcm_params_set_min(struct pcm_params * pcm_params,enum pcm_param param,unsigned int val)750*d0c94b83SXin Li void pcm_params_set_min(struct pcm_params *pcm_params,
751*d0c94b83SXin Li enum pcm_param param, unsigned int val)
752*d0c94b83SXin Li {
753*d0c94b83SXin Li struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
754*d0c94b83SXin Li int p;
755*d0c94b83SXin Li
756*d0c94b83SXin Li if (!params)
757*d0c94b83SXin Li return;
758*d0c94b83SXin Li
759*d0c94b83SXin Li p = pcm_param_to_alsa(param);
760*d0c94b83SXin Li if (p < 0)
761*d0c94b83SXin Li return;
762*d0c94b83SXin Li
763*d0c94b83SXin Li param_set_min(params, p, val);
764*d0c94b83SXin Li }
765*d0c94b83SXin Li
pcm_params_get_max(const struct pcm_params * pcm_params,enum pcm_param param)766*d0c94b83SXin Li unsigned int pcm_params_get_max(const struct pcm_params *pcm_params,
767*d0c94b83SXin Li enum pcm_param param)
768*d0c94b83SXin Li {
769*d0c94b83SXin Li struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
770*d0c94b83SXin Li int p;
771*d0c94b83SXin Li
772*d0c94b83SXin Li if (!params)
773*d0c94b83SXin Li return 0;
774*d0c94b83SXin Li
775*d0c94b83SXin Li p = pcm_param_to_alsa(param);
776*d0c94b83SXin Li if (p < 0)
777*d0c94b83SXin Li return 0;
778*d0c94b83SXin Li
779*d0c94b83SXin Li return param_get_max(params, p);
780*d0c94b83SXin Li }
781*d0c94b83SXin Li
pcm_params_set_max(struct pcm_params * pcm_params,enum pcm_param param,unsigned int val)782*d0c94b83SXin Li void pcm_params_set_max(struct pcm_params *pcm_params,
783*d0c94b83SXin Li enum pcm_param param, unsigned int val)
784*d0c94b83SXin Li {
785*d0c94b83SXin Li struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
786*d0c94b83SXin Li int p;
787*d0c94b83SXin Li
788*d0c94b83SXin Li if (!params)
789*d0c94b83SXin Li return;
790*d0c94b83SXin Li
791*d0c94b83SXin Li p = pcm_param_to_alsa(param);
792*d0c94b83SXin Li if (p < 0)
793*d0c94b83SXin Li return;
794*d0c94b83SXin Li
795*d0c94b83SXin Li param_set_max(params, p, val);
796*d0c94b83SXin Li }
797*d0c94b83SXin Li
pcm_mask_test(struct pcm_mask * m,unsigned int index)798*d0c94b83SXin Li static int pcm_mask_test(struct pcm_mask *m, unsigned int index)
799*d0c94b83SXin Li {
800*d0c94b83SXin Li const unsigned int bitshift = 5; /* for 32 bit integer */
801*d0c94b83SXin Li const unsigned int bitmask = (1 << bitshift) - 1;
802*d0c94b83SXin Li unsigned int element;
803*d0c94b83SXin Li
804*d0c94b83SXin Li element = index >> bitshift;
805*d0c94b83SXin Li if (element >= ARRAY_SIZE(m->bits))
806*d0c94b83SXin Li return 0; /* for safety, but should never occur */
807*d0c94b83SXin Li return (m->bits[element] >> (index & bitmask)) & 1;
808*d0c94b83SXin Li }
809*d0c94b83SXin Li
pcm_mask_to_string(struct pcm_mask * m,char * string,unsigned int size,char * mask_name,const char * const * bit_array_name,size_t bit_array_size)810*d0c94b83SXin Li static int pcm_mask_to_string(struct pcm_mask *m, char *string, unsigned int size,
811*d0c94b83SXin Li char *mask_name,
812*d0c94b83SXin Li const char * const *bit_array_name, size_t bit_array_size)
813*d0c94b83SXin Li {
814*d0c94b83SXin Li unsigned int i;
815*d0c94b83SXin Li unsigned int offset = 0;
816*d0c94b83SXin Li
817*d0c94b83SXin Li if (m == NULL)
818*d0c94b83SXin Li return 0;
819*d0c94b83SXin Li if (bit_array_size < 32) {
820*d0c94b83SXin Li STRLOG(string, offset, size, "%12s:\t%#08x\n", mask_name, m->bits[0]);
821*d0c94b83SXin Li } else { /* spans two or more bitfields, print with an array index */
822*d0c94b83SXin Li for (i = 0; i < (bit_array_size + 31) >> 5; ++i) {
823*d0c94b83SXin Li STRLOG(string, offset, size, "%9s[%d]:\t%#08x\n",
824*d0c94b83SXin Li mask_name, i, m->bits[i]);
825*d0c94b83SXin Li }
826*d0c94b83SXin Li }
827*d0c94b83SXin Li for (i = 0; i < bit_array_size; ++i) {
828*d0c94b83SXin Li if (pcm_mask_test(m, i)) {
829*d0c94b83SXin Li STRLOG(string, offset, size, "%12s \t%s\n", "", bit_array_name[i]);
830*d0c94b83SXin Li }
831*d0c94b83SXin Li }
832*d0c94b83SXin Li return offset;
833*d0c94b83SXin Li }
834*d0c94b83SXin Li
pcm_params_to_string(struct pcm_params * params,char * string,unsigned int size)835*d0c94b83SXin Li int pcm_params_to_string(struct pcm_params *params, char *string, unsigned int size)
836*d0c94b83SXin Li {
837*d0c94b83SXin Li struct pcm_mask *m;
838*d0c94b83SXin Li unsigned int min, max;
839*d0c94b83SXin Li unsigned int clipoffset, offset;
840*d0c94b83SXin Li
841*d0c94b83SXin Li m = pcm_params_get_mask(params, PCM_PARAM_ACCESS);
842*d0c94b83SXin Li offset = pcm_mask_to_string(m, string, size,
843*d0c94b83SXin Li "Access", access_lookup, ARRAY_SIZE(access_lookup));
844*d0c94b83SXin Li m = pcm_params_get_mask(params, PCM_PARAM_FORMAT);
845*d0c94b83SXin Li clipoffset = offset > size ? size : offset;
846*d0c94b83SXin Li offset += pcm_mask_to_string(m, string + clipoffset, size - clipoffset,
847*d0c94b83SXin Li "Format", format_lookup, ARRAY_SIZE(format_lookup));
848*d0c94b83SXin Li m = pcm_params_get_mask(params, PCM_PARAM_SUBFORMAT);
849*d0c94b83SXin Li clipoffset = offset > size ? size : offset;
850*d0c94b83SXin Li offset += pcm_mask_to_string(m, string + clipoffset, size - clipoffset,
851*d0c94b83SXin Li "Subformat", subformat_lookup, ARRAY_SIZE(subformat_lookup));
852*d0c94b83SXin Li min = pcm_params_get_min(params, PCM_PARAM_RATE);
853*d0c94b83SXin Li max = pcm_params_get_max(params, PCM_PARAM_RATE);
854*d0c94b83SXin Li STRLOG(string, offset, size, " Rate:\tmin=%uHz\tmax=%uHz\n", min, max);
855*d0c94b83SXin Li min = pcm_params_get_min(params, PCM_PARAM_CHANNELS);
856*d0c94b83SXin Li max = pcm_params_get_max(params, PCM_PARAM_CHANNELS);
857*d0c94b83SXin Li STRLOG(string, offset, size, " Channels:\tmin=%u\t\tmax=%u\n", min, max);
858*d0c94b83SXin Li min = pcm_params_get_min(params, PCM_PARAM_SAMPLE_BITS);
859*d0c94b83SXin Li max = pcm_params_get_max(params, PCM_PARAM_SAMPLE_BITS);
860*d0c94b83SXin Li STRLOG(string, offset, size, " Sample bits:\tmin=%u\t\tmax=%u\n", min, max);
861*d0c94b83SXin Li min = pcm_params_get_min(params, PCM_PARAM_PERIOD_SIZE);
862*d0c94b83SXin Li max = pcm_params_get_max(params, PCM_PARAM_PERIOD_SIZE);
863*d0c94b83SXin Li STRLOG(string, offset, size, " Period size:\tmin=%u\t\tmax=%u\n", min, max);
864*d0c94b83SXin Li min = pcm_params_get_min(params, PCM_PARAM_PERIODS);
865*d0c94b83SXin Li max = pcm_params_get_max(params, PCM_PARAM_PERIODS);
866*d0c94b83SXin Li STRLOG(string, offset, size, "Period count:\tmin=%u\t\tmax=%u\n", min, max);
867*d0c94b83SXin Li return offset;
868*d0c94b83SXin Li }
869*d0c94b83SXin Li
pcm_params_format_test(struct pcm_params * params,enum pcm_format format)870*d0c94b83SXin Li int pcm_params_format_test(struct pcm_params *params, enum pcm_format format)
871*d0c94b83SXin Li {
872*d0c94b83SXin Li unsigned int alsa_format = pcm_format_to_alsa(format);
873*d0c94b83SXin Li
874*d0c94b83SXin Li if (alsa_format == SNDRV_PCM_FORMAT_S16_LE && format != PCM_FORMAT_S16_LE)
875*d0c94b83SXin Li return 0; /* caution: format not recognized is equivalent to S16_LE */
876*d0c94b83SXin Li return pcm_mask_test(pcm_params_get_mask(params, PCM_PARAM_FORMAT), alsa_format);
877*d0c94b83SXin Li }
878*d0c94b83SXin Li
pcm_close(struct pcm * pcm)879*d0c94b83SXin Li int pcm_close(struct pcm *pcm)
880*d0c94b83SXin Li {
881*d0c94b83SXin Li if (pcm == &bad_pcm)
882*d0c94b83SXin Li return 0;
883*d0c94b83SXin Li
884*d0c94b83SXin Li pcm_hw_munmap_status(pcm);
885*d0c94b83SXin Li
886*d0c94b83SXin Li if (pcm->flags & PCM_MMAP) {
887*d0c94b83SXin Li pcm_stop(pcm);
888*d0c94b83SXin Li pcm->ops->munmap(pcm->data, pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
889*d0c94b83SXin Li }
890*d0c94b83SXin Li
891*d0c94b83SXin Li if (pcm->data)
892*d0c94b83SXin Li pcm->ops->close(pcm->data);
893*d0c94b83SXin Li if (pcm->snd_node)
894*d0c94b83SXin Li snd_utils_put_dev_node(pcm->snd_node);
895*d0c94b83SXin Li
896*d0c94b83SXin Li pcm->prepared = false;
897*d0c94b83SXin Li pcm->running = false;
898*d0c94b83SXin Li pcm->buffer_size = 0;
899*d0c94b83SXin Li pcm->fd = -1;
900*d0c94b83SXin Li free(pcm);
901*d0c94b83SXin Li return 0;
902*d0c94b83SXin Li }
903*d0c94b83SXin Li
pcm_open(unsigned int card,unsigned int device,unsigned int flags,struct pcm_config * config)904*d0c94b83SXin Li struct pcm *pcm_open(unsigned int card, unsigned int device,
905*d0c94b83SXin Li unsigned int flags, struct pcm_config *config)
906*d0c94b83SXin Li {
907*d0c94b83SXin Li struct pcm *pcm;
908*d0c94b83SXin Li struct snd_pcm_info info;
909*d0c94b83SXin Li struct snd_pcm_hw_params params;
910*d0c94b83SXin Li struct snd_pcm_sw_params sparams;
911*d0c94b83SXin Li int rc, pcm_type;
912*d0c94b83SXin Li
913*d0c94b83SXin Li if (!config) {
914*d0c94b83SXin Li return &bad_pcm; /* TODO: could support default config here */
915*d0c94b83SXin Li }
916*d0c94b83SXin Li pcm = calloc(1, sizeof(struct pcm));
917*d0c94b83SXin Li if (!pcm) {
918*d0c94b83SXin Li oops(&bad_pcm, ENOMEM, "can't allocate PCM object");
919*d0c94b83SXin Li return &bad_pcm; /* TODO: could support default config here */
920*d0c94b83SXin Li }
921*d0c94b83SXin Li
922*d0c94b83SXin Li pcm->config = *config;
923*d0c94b83SXin Li pcm->flags = flags;
924*d0c94b83SXin Li
925*d0c94b83SXin Li pcm->snd_node = snd_utils_get_dev_node(card, device, NODE_PCM);
926*d0c94b83SXin Li pcm_type = snd_utils_get_node_type(pcm->snd_node);
927*d0c94b83SXin Li if (pcm_type == SND_NODE_TYPE_PLUGIN)
928*d0c94b83SXin Li pcm->ops = &plug_ops;
929*d0c94b83SXin Li else
930*d0c94b83SXin Li pcm->ops = &hw_ops;
931*d0c94b83SXin Li
932*d0c94b83SXin Li pcm->fd = pcm->ops->open(card, device, flags, &pcm->data, pcm->snd_node);
933*d0c94b83SXin Li if (pcm->fd < 0) {
934*d0c94b83SXin Li oops(&bad_pcm, errno, "cannot open device %u for card %u",
935*d0c94b83SXin Li device, card);
936*d0c94b83SXin Li goto fail_open;
937*d0c94b83SXin Li }
938*d0c94b83SXin Li
939*d0c94b83SXin Li if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_INFO, &info)) {
940*d0c94b83SXin Li oops(&bad_pcm, errno, "cannot get info");
941*d0c94b83SXin Li goto fail_close;
942*d0c94b83SXin Li }
943*d0c94b83SXin Li pcm->subdevice = info.subdevice;
944*d0c94b83SXin Li
945*d0c94b83SXin Li param_init(¶ms);
946*d0c94b83SXin Li param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT,
947*d0c94b83SXin Li pcm_format_to_alsa(config->format));
948*d0c94b83SXin Li param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT,
949*d0c94b83SXin Li SNDRV_PCM_SUBFORMAT_STD);
950*d0c94b83SXin Li param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
951*d0c94b83SXin Li param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
952*d0c94b83SXin Li pcm_format_to_bits(config->format));
953*d0c94b83SXin Li param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS,
954*d0c94b83SXin Li pcm_format_to_bits(config->format) * config->channels);
955*d0c94b83SXin Li param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS,
956*d0c94b83SXin Li config->channels);
957*d0c94b83SXin Li param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
958*d0c94b83SXin Li param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate);
959*d0c94b83SXin Li
960*d0c94b83SXin Li if (flags & PCM_NOIRQ) {
961*d0c94b83SXin Li if (!(flags & PCM_MMAP)) {
962*d0c94b83SXin Li oops(&bad_pcm, EINVAL, "noirq only currently supported with mmap().");
963*d0c94b83SXin Li goto fail_close;
964*d0c94b83SXin Li }
965*d0c94b83SXin Li
966*d0c94b83SXin Li params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
967*d0c94b83SXin Li pcm->noirq_frames_per_msec = config->rate / 1000;
968*d0c94b83SXin Li }
969*d0c94b83SXin Li
970*d0c94b83SXin Li if (flags & PCM_MMAP)
971*d0c94b83SXin Li param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS,
972*d0c94b83SXin Li SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
973*d0c94b83SXin Li else
974*d0c94b83SXin Li param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS,
975*d0c94b83SXin Li SNDRV_PCM_ACCESS_RW_INTERLEAVED);
976*d0c94b83SXin Li
977*d0c94b83SXin Li if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) {
978*d0c94b83SXin Li oops(&bad_pcm, errno, "cannot set hw params");
979*d0c94b83SXin Li goto fail_close;
980*d0c94b83SXin Li }
981*d0c94b83SXin Li
982*d0c94b83SXin Li /* get our refined hw_params */
983*d0c94b83SXin Li config->period_size = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
984*d0c94b83SXin Li config->period_count = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS);
985*d0c94b83SXin Li pcm->buffer_size = config->period_count * config->period_size;
986*d0c94b83SXin Li
987*d0c94b83SXin Li if (flags & PCM_MMAP) {
988*d0c94b83SXin Li pcm->mmap_buffer = pcm->ops->mmap(pcm->data, NULL,
989*d0c94b83SXin Li pcm_frames_to_bytes(pcm, pcm->buffer_size),
990*d0c94b83SXin Li PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, 0);
991*d0c94b83SXin Li if (pcm->mmap_buffer == MAP_FAILED) {
992*d0c94b83SXin Li oops(&bad_pcm, errno, "failed to mmap buffer %d bytes\n",
993*d0c94b83SXin Li pcm_frames_to_bytes(pcm, pcm->buffer_size));
994*d0c94b83SXin Li goto fail_close;
995*d0c94b83SXin Li }
996*d0c94b83SXin Li }
997*d0c94b83SXin Li
998*d0c94b83SXin Li memset(&sparams, 0, sizeof(sparams));
999*d0c94b83SXin Li sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
1000*d0c94b83SXin Li sparams.period_step = 1;
1001*d0c94b83SXin Li
1002*d0c94b83SXin Li if (!config->start_threshold) {
1003*d0c94b83SXin Li if (pcm->flags & PCM_IN)
1004*d0c94b83SXin Li pcm->config.start_threshold = sparams.start_threshold = 1;
1005*d0c94b83SXin Li else
1006*d0c94b83SXin Li pcm->config.start_threshold = sparams.start_threshold =
1007*d0c94b83SXin Li config->period_count * config->period_size / 2;
1008*d0c94b83SXin Li } else
1009*d0c94b83SXin Li sparams.start_threshold = config->start_threshold;
1010*d0c94b83SXin Li
1011*d0c94b83SXin Li /* pick a high stop threshold - todo: does this need further tuning */
1012*d0c94b83SXin Li if (!config->stop_threshold) {
1013*d0c94b83SXin Li if (pcm->flags & PCM_IN)
1014*d0c94b83SXin Li pcm->config.stop_threshold = sparams.stop_threshold =
1015*d0c94b83SXin Li config->period_count * config->period_size * 10;
1016*d0c94b83SXin Li else
1017*d0c94b83SXin Li pcm->config.stop_threshold = sparams.stop_threshold =
1018*d0c94b83SXin Li config->period_count * config->period_size;
1019*d0c94b83SXin Li }
1020*d0c94b83SXin Li else
1021*d0c94b83SXin Li sparams.stop_threshold = config->stop_threshold;
1022*d0c94b83SXin Li
1023*d0c94b83SXin Li if (!pcm->config.avail_min) {
1024*d0c94b83SXin Li if (pcm->flags & PCM_MMAP)
1025*d0c94b83SXin Li pcm->config.avail_min = sparams.avail_min = pcm->config.period_size;
1026*d0c94b83SXin Li else
1027*d0c94b83SXin Li pcm->config.avail_min = sparams.avail_min = 1;
1028*d0c94b83SXin Li } else
1029*d0c94b83SXin Li sparams.avail_min = config->avail_min;
1030*d0c94b83SXin Li
1031*d0c94b83SXin Li sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
1032*d0c94b83SXin Li sparams.silence_threshold = config->silence_threshold;
1033*d0c94b83SXin Li sparams.silence_size = config->silence_size;
1034*d0c94b83SXin Li
1035*d0c94b83SXin Li if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
1036*d0c94b83SXin Li oops(&bad_pcm, errno, "cannot set sw params");
1037*d0c94b83SXin Li goto fail;
1038*d0c94b83SXin Li }
1039*d0c94b83SXin Li pcm->boundary = sparams.boundary;
1040*d0c94b83SXin Li
1041*d0c94b83SXin Li rc = pcm_hw_mmap_status(pcm);
1042*d0c94b83SXin Li if (rc < 0) {
1043*d0c94b83SXin Li oops(&bad_pcm, errno, "mmap status failed");
1044*d0c94b83SXin Li goto fail;
1045*d0c94b83SXin Li }
1046*d0c94b83SXin Li
1047*d0c94b83SXin Li #ifdef SNDRV_PCM_IOCTL_TTSTAMP
1048*d0c94b83SXin Li if (pcm->flags & PCM_MONOTONIC) {
1049*d0c94b83SXin Li int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
1050*d0c94b83SXin Li rc = pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
1051*d0c94b83SXin Li if (rc < 0) {
1052*d0c94b83SXin Li oops(&bad_pcm, errno, "cannot set timestamp type");
1053*d0c94b83SXin Li goto fail;
1054*d0c94b83SXin Li }
1055*d0c94b83SXin Li }
1056*d0c94b83SXin Li #endif
1057*d0c94b83SXin Li
1058*d0c94b83SXin Li pcm->xruns = 0;
1059*d0c94b83SXin Li return pcm;
1060*d0c94b83SXin Li
1061*d0c94b83SXin Li fail:
1062*d0c94b83SXin Li if (flags & PCM_MMAP)
1063*d0c94b83SXin Li pcm->ops->munmap(pcm->data, pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
1064*d0c94b83SXin Li fail_close:
1065*d0c94b83SXin Li pcm->ops->close(pcm->data);
1066*d0c94b83SXin Li
1067*d0c94b83SXin Li fail_open:
1068*d0c94b83SXin Li snd_utils_put_dev_node(pcm->snd_node);
1069*d0c94b83SXin Li free(pcm);
1070*d0c94b83SXin Li return &bad_pcm;
1071*d0c94b83SXin Li }
1072*d0c94b83SXin Li
pcm_is_ready(struct pcm * pcm)1073*d0c94b83SXin Li int pcm_is_ready(struct pcm *pcm)
1074*d0c94b83SXin Li {
1075*d0c94b83SXin Li return pcm->fd >= 0;
1076*d0c94b83SXin Li }
1077*d0c94b83SXin Li
pcm_prepare(struct pcm * pcm)1078*d0c94b83SXin Li int pcm_prepare(struct pcm *pcm)
1079*d0c94b83SXin Li {
1080*d0c94b83SXin Li if (pcm->prepared)
1081*d0c94b83SXin Li return 0;
1082*d0c94b83SXin Li
1083*d0c94b83SXin Li if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_PREPARE) < 0)
1084*d0c94b83SXin Li return oops(pcm, errno, "cannot prepare channel");
1085*d0c94b83SXin Li
1086*d0c94b83SXin Li pcm->prepared = true;
1087*d0c94b83SXin Li return 0;
1088*d0c94b83SXin Li }
1089*d0c94b83SXin Li
pcm_start(struct pcm * pcm)1090*d0c94b83SXin Li int pcm_start(struct pcm *pcm)
1091*d0c94b83SXin Li {
1092*d0c94b83SXin Li int prepare_error = pcm_prepare(pcm);
1093*d0c94b83SXin Li if (prepare_error)
1094*d0c94b83SXin Li return prepare_error;
1095*d0c94b83SXin Li
1096*d0c94b83SXin Li if (pcm->flags & PCM_MMAP)
1097*d0c94b83SXin Li pcm_sync_ptr(pcm, 0);
1098*d0c94b83SXin Li
1099*d0c94b83SXin Li if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_START) < 0)
1100*d0c94b83SXin Li return oops(pcm, errno, "cannot start channel");
1101*d0c94b83SXin Li
1102*d0c94b83SXin Li pcm->running = true;
1103*d0c94b83SXin Li return 0;
1104*d0c94b83SXin Li }
1105*d0c94b83SXin Li
pcm_stop(struct pcm * pcm)1106*d0c94b83SXin Li int pcm_stop(struct pcm *pcm)
1107*d0c94b83SXin Li {
1108*d0c94b83SXin Li if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_DROP) < 0)
1109*d0c94b83SXin Li return oops(pcm, errno, "cannot stop channel");
1110*d0c94b83SXin Li
1111*d0c94b83SXin Li pcm->prepared = false;
1112*d0c94b83SXin Li pcm->running = false;
1113*d0c94b83SXin Li return 0;
1114*d0c94b83SXin Li }
1115*d0c94b83SXin Li
pcm_mmap_playback_avail(struct pcm * pcm)1116*d0c94b83SXin Li static inline long pcm_mmap_playback_avail(struct pcm *pcm)
1117*d0c94b83SXin Li {
1118*d0c94b83SXin Li long avail = pcm->mmap_status->hw_ptr + (unsigned long) pcm->buffer_size -
1119*d0c94b83SXin Li pcm->mmap_control->appl_ptr;
1120*d0c94b83SXin Li
1121*d0c94b83SXin Li if (avail < 0) {
1122*d0c94b83SXin Li avail += pcm->boundary;
1123*d0c94b83SXin Li } else if ((unsigned long) avail >= pcm->boundary) {
1124*d0c94b83SXin Li avail -= pcm->boundary;
1125*d0c94b83SXin Li }
1126*d0c94b83SXin Li
1127*d0c94b83SXin Li return avail;
1128*d0c94b83SXin Li }
1129*d0c94b83SXin Li
pcm_mmap_capture_avail(struct pcm * pcm)1130*d0c94b83SXin Li static inline long pcm_mmap_capture_avail(struct pcm *pcm)
1131*d0c94b83SXin Li {
1132*d0c94b83SXin Li long avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr;
1133*d0c94b83SXin Li if (avail < 0) {
1134*d0c94b83SXin Li avail += pcm->boundary;
1135*d0c94b83SXin Li }
1136*d0c94b83SXin Li return avail;
1137*d0c94b83SXin Li }
1138*d0c94b83SXin Li
pcm_mmap_avail(struct pcm * pcm)1139*d0c94b83SXin Li int pcm_mmap_avail(struct pcm *pcm)
1140*d0c94b83SXin Li {
1141*d0c94b83SXin Li pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
1142*d0c94b83SXin Li if (pcm->flags & PCM_IN) {
1143*d0c94b83SXin Li return (int) pcm_mmap_capture_avail(pcm);
1144*d0c94b83SXin Li } else {
1145*d0c94b83SXin Li return (int) pcm_mmap_playback_avail(pcm);
1146*d0c94b83SXin Li }
1147*d0c94b83SXin Li }
1148*d0c94b83SXin Li
pcm_mmap_appl_forward(struct pcm * pcm,int frames)1149*d0c94b83SXin Li static void pcm_mmap_appl_forward(struct pcm *pcm, int frames)
1150*d0c94b83SXin Li {
1151*d0c94b83SXin Li unsigned long appl_ptr = pcm->mmap_control->appl_ptr;
1152*d0c94b83SXin Li appl_ptr += frames;
1153*d0c94b83SXin Li
1154*d0c94b83SXin Li /* check for boundary wrap */
1155*d0c94b83SXin Li if (appl_ptr >= pcm->boundary) {
1156*d0c94b83SXin Li appl_ptr -= pcm->boundary;
1157*d0c94b83SXin Li }
1158*d0c94b83SXin Li pcm->mmap_control->appl_ptr = appl_ptr;
1159*d0c94b83SXin Li }
1160*d0c94b83SXin Li
pcm_mmap_begin(struct pcm * pcm,void ** areas,unsigned int * offset,unsigned int * frames)1161*d0c94b83SXin Li int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
1162*d0c94b83SXin Li unsigned int *frames)
1163*d0c94b83SXin Li {
1164*d0c94b83SXin Li unsigned int continuous, copy_frames, avail;
1165*d0c94b83SXin Li
1166*d0c94b83SXin Li /* return the mmap buffer */
1167*d0c94b83SXin Li *areas = pcm->mmap_buffer;
1168*d0c94b83SXin Li
1169*d0c94b83SXin Li /* and the application offset in frames */
1170*d0c94b83SXin Li *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size;
1171*d0c94b83SXin Li
1172*d0c94b83SXin Li avail = pcm_mmap_avail(pcm);
1173*d0c94b83SXin Li if (avail > pcm->buffer_size)
1174*d0c94b83SXin Li avail = pcm->buffer_size;
1175*d0c94b83SXin Li continuous = pcm->buffer_size - *offset;
1176*d0c94b83SXin Li
1177*d0c94b83SXin Li /* we can only copy frames if the are availabale and continuos */
1178*d0c94b83SXin Li copy_frames = *frames;
1179*d0c94b83SXin Li if (copy_frames > avail)
1180*d0c94b83SXin Li copy_frames = avail;
1181*d0c94b83SXin Li if (copy_frames > continuous)
1182*d0c94b83SXin Li copy_frames = continuous;
1183*d0c94b83SXin Li *frames = copy_frames;
1184*d0c94b83SXin Li
1185*d0c94b83SXin Li return 0;
1186*d0c94b83SXin Li }
1187*d0c94b83SXin Li
pcm_mmap_commit(struct pcm * pcm,unsigned int offset,unsigned int frames)1188*d0c94b83SXin Li int pcm_mmap_commit(struct pcm *pcm, unsigned int offset __attribute__((unused)), unsigned int frames)
1189*d0c94b83SXin Li {
1190*d0c94b83SXin Li /* update the application pointer in userspace and kernel */
1191*d0c94b83SXin Li pcm_mmap_appl_forward(pcm, frames);
1192*d0c94b83SXin Li pcm_sync_ptr(pcm, 0);
1193*d0c94b83SXin Li
1194*d0c94b83SXin Li return frames;
1195*d0c94b83SXin Li }
1196*d0c94b83SXin Li
pcm_avail_update(struct pcm * pcm)1197*d0c94b83SXin Li int pcm_avail_update(struct pcm *pcm)
1198*d0c94b83SXin Li {
1199*d0c94b83SXin Li pcm_sync_ptr(pcm, 0);
1200*d0c94b83SXin Li return pcm_mmap_avail(pcm);
1201*d0c94b83SXin Li }
1202*d0c94b83SXin Li
pcm_state(struct pcm * pcm)1203*d0c94b83SXin Li int pcm_state(struct pcm *pcm)
1204*d0c94b83SXin Li {
1205*d0c94b83SXin Li int err = pcm_sync_ptr(pcm, 0);
1206*d0c94b83SXin Li if (err < 0)
1207*d0c94b83SXin Li return err;
1208*d0c94b83SXin Li
1209*d0c94b83SXin Li return pcm->mmap_status->state;
1210*d0c94b83SXin Li }
1211*d0c94b83SXin Li
pcm_set_avail_min(struct pcm * pcm,int avail_min)1212*d0c94b83SXin Li int pcm_set_avail_min(struct pcm *pcm, int avail_min)
1213*d0c94b83SXin Li {
1214*d0c94b83SXin Li if ((~pcm->flags) & (PCM_MMAP | PCM_NOIRQ))
1215*d0c94b83SXin Li return -ENOSYS;
1216*d0c94b83SXin Li
1217*d0c94b83SXin Li pcm->config.avail_min = avail_min;
1218*d0c94b83SXin Li return 0;
1219*d0c94b83SXin Li }
1220*d0c94b83SXin Li
pcm_wait(struct pcm * pcm,int timeout)1221*d0c94b83SXin Li int pcm_wait(struct pcm *pcm, int timeout)
1222*d0c94b83SXin Li {
1223*d0c94b83SXin Li struct pollfd pfd;
1224*d0c94b83SXin Li int err;
1225*d0c94b83SXin Li
1226*d0c94b83SXin Li pfd.fd = pcm->fd;
1227*d0c94b83SXin Li pfd.events = POLLOUT | POLLERR | POLLNVAL;
1228*d0c94b83SXin Li
1229*d0c94b83SXin Li do {
1230*d0c94b83SXin Li /* let's wait for avail or timeout */
1231*d0c94b83SXin Li err = pcm->ops->poll(pcm->data, &pfd, 1, timeout);
1232*d0c94b83SXin Li if (err < 0)
1233*d0c94b83SXin Li return -errno;
1234*d0c94b83SXin Li
1235*d0c94b83SXin Li /* timeout ? */
1236*d0c94b83SXin Li if (err == 0)
1237*d0c94b83SXin Li return 0;
1238*d0c94b83SXin Li
1239*d0c94b83SXin Li /* have we been interrupted ? */
1240*d0c94b83SXin Li if (errno == -EINTR)
1241*d0c94b83SXin Li continue;
1242*d0c94b83SXin Li
1243*d0c94b83SXin Li /* check for any errors */
1244*d0c94b83SXin Li if (pfd.revents & (POLLERR | POLLNVAL)) {
1245*d0c94b83SXin Li switch (pcm_state(pcm)) {
1246*d0c94b83SXin Li case PCM_STATE_XRUN:
1247*d0c94b83SXin Li return -EPIPE;
1248*d0c94b83SXin Li case PCM_STATE_SUSPENDED:
1249*d0c94b83SXin Li return -ESTRPIPE;
1250*d0c94b83SXin Li case PCM_STATE_DISCONNECTED:
1251*d0c94b83SXin Li return -ENODEV;
1252*d0c94b83SXin Li default:
1253*d0c94b83SXin Li return -EIO;
1254*d0c94b83SXin Li }
1255*d0c94b83SXin Li }
1256*d0c94b83SXin Li /* poll again if fd not ready for IO */
1257*d0c94b83SXin Li } while (!(pfd.revents & (POLLIN | POLLOUT)));
1258*d0c94b83SXin Li
1259*d0c94b83SXin Li return 1;
1260*d0c94b83SXin Li }
1261*d0c94b83SXin Li
pcm_get_poll_fd(struct pcm * pcm)1262*d0c94b83SXin Li int pcm_get_poll_fd(struct pcm *pcm)
1263*d0c94b83SXin Li {
1264*d0c94b83SXin Li return pcm->fd;
1265*d0c94b83SXin Li }
1266*d0c94b83SXin Li
pcm_mmap_transfer(struct pcm * pcm,const void * buffer,unsigned int bytes)1267*d0c94b83SXin Li int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes)
1268*d0c94b83SXin Li {
1269*d0c94b83SXin Li int err = 0, frames, avail;
1270*d0c94b83SXin Li unsigned int offset = 0, count;
1271*d0c94b83SXin Li
1272*d0c94b83SXin Li if (bytes == 0)
1273*d0c94b83SXin Li return 0;
1274*d0c94b83SXin Li
1275*d0c94b83SXin Li count = pcm_bytes_to_frames(pcm, bytes);
1276*d0c94b83SXin Li
1277*d0c94b83SXin Li while (count > 0) {
1278*d0c94b83SXin Li
1279*d0c94b83SXin Li /* get the available space for writing new frames */
1280*d0c94b83SXin Li avail = pcm_avail_update(pcm);
1281*d0c94b83SXin Li if (avail < 0) {
1282*d0c94b83SXin Li fprintf(stderr, "cannot determine available mmap frames");
1283*d0c94b83SXin Li return err;
1284*d0c94b83SXin Li }
1285*d0c94b83SXin Li
1286*d0c94b83SXin Li /* start the audio if we reach the threshold */
1287*d0c94b83SXin Li if (!pcm->running &&
1288*d0c94b83SXin Li (pcm->buffer_size - avail) >= pcm->config.start_threshold) {
1289*d0c94b83SXin Li if (pcm_start(pcm) < 0) {
1290*d0c94b83SXin Li fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n",
1291*d0c94b83SXin Li (unsigned int)pcm->mmap_status->hw_ptr,
1292*d0c94b83SXin Li (unsigned int)pcm->mmap_control->appl_ptr,
1293*d0c94b83SXin Li avail);
1294*d0c94b83SXin Li return -errno;
1295*d0c94b83SXin Li }
1296*d0c94b83SXin Li pcm->wait_for_avail_min = 0;
1297*d0c94b83SXin Li }
1298*d0c94b83SXin Li
1299*d0c94b83SXin Li /* sleep until we have space to write new frames */
1300*d0c94b83SXin Li if (pcm->running) {
1301*d0c94b83SXin Li /* enable waiting for avail_min threshold when less frames than we have to write
1302*d0c94b83SXin Li * are available. */
1303*d0c94b83SXin Li if (!pcm->wait_for_avail_min && (count > (unsigned int)avail))
1304*d0c94b83SXin Li pcm->wait_for_avail_min = 1;
1305*d0c94b83SXin Li
1306*d0c94b83SXin Li if (pcm->wait_for_avail_min && (avail < pcm->config.avail_min)) {
1307*d0c94b83SXin Li int time = -1;
1308*d0c94b83SXin Li
1309*d0c94b83SXin Li /* disable waiting for avail_min threshold to allow small amounts of data to be
1310*d0c94b83SXin Li * written without waiting as long as there is enough room in buffer. */
1311*d0c94b83SXin Li pcm->wait_for_avail_min = 0;
1312*d0c94b83SXin Li
1313*d0c94b83SXin Li if (pcm->flags & PCM_NOIRQ)
1314*d0c94b83SXin Li time = (pcm->config.avail_min - avail) / pcm->noirq_frames_per_msec;
1315*d0c94b83SXin Li
1316*d0c94b83SXin Li err = pcm_wait(pcm, time);
1317*d0c94b83SXin Li if (err < 0) {
1318*d0c94b83SXin Li pcm->prepared = false;
1319*d0c94b83SXin Li pcm->running = false;
1320*d0c94b83SXin Li oops(pcm, errno, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
1321*d0c94b83SXin Li (unsigned int)pcm->mmap_status->hw_ptr,
1322*d0c94b83SXin Li (unsigned int)pcm->mmap_control->appl_ptr,
1323*d0c94b83SXin Li avail);
1324*d0c94b83SXin Li pcm->mmap_control->appl_ptr = 0;
1325*d0c94b83SXin Li return err;
1326*d0c94b83SXin Li }
1327*d0c94b83SXin Li continue;
1328*d0c94b83SXin Li }
1329*d0c94b83SXin Li }
1330*d0c94b83SXin Li
1331*d0c94b83SXin Li frames = count;
1332*d0c94b83SXin Li if (frames > avail)
1333*d0c94b83SXin Li frames = avail;
1334*d0c94b83SXin Li
1335*d0c94b83SXin Li if (!frames)
1336*d0c94b83SXin Li break;
1337*d0c94b83SXin Li
1338*d0c94b83SXin Li /* copy frames from buffer */
1339*d0c94b83SXin Li frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames);
1340*d0c94b83SXin Li if (frames < 0) {
1341*d0c94b83SXin Li fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
1342*d0c94b83SXin Li (unsigned int)pcm->mmap_status->hw_ptr,
1343*d0c94b83SXin Li (unsigned int)pcm->mmap_control->appl_ptr,
1344*d0c94b83SXin Li avail);
1345*d0c94b83SXin Li return frames;
1346*d0c94b83SXin Li }
1347*d0c94b83SXin Li
1348*d0c94b83SXin Li offset += frames;
1349*d0c94b83SXin Li count -= frames;
1350*d0c94b83SXin Li }
1351*d0c94b83SXin Li
1352*d0c94b83SXin Li return 0;
1353*d0c94b83SXin Li }
1354*d0c94b83SXin Li
pcm_mmap_write(struct pcm * pcm,const void * data,unsigned int count)1355*d0c94b83SXin Li int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count)
1356*d0c94b83SXin Li {
1357*d0c94b83SXin Li if ((~pcm->flags) & (PCM_OUT | PCM_MMAP))
1358*d0c94b83SXin Li return -ENOSYS;
1359*d0c94b83SXin Li
1360*d0c94b83SXin Li return pcm_mmap_transfer(pcm, (void *)data, count);
1361*d0c94b83SXin Li }
1362*d0c94b83SXin Li
pcm_mmap_read(struct pcm * pcm,void * data,unsigned int count)1363*d0c94b83SXin Li int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count)
1364*d0c94b83SXin Li {
1365*d0c94b83SXin Li if ((~pcm->flags) & (PCM_IN | PCM_MMAP))
1366*d0c94b83SXin Li return -ENOSYS;
1367*d0c94b83SXin Li
1368*d0c94b83SXin Li return pcm_mmap_transfer(pcm, data, count);
1369*d0c94b83SXin Li }
1370*d0c94b83SXin Li
pcm_ioctl(struct pcm * pcm,int request,...)1371*d0c94b83SXin Li int pcm_ioctl(struct pcm *pcm, int request, ...)
1372*d0c94b83SXin Li {
1373*d0c94b83SXin Li va_list ap;
1374*d0c94b83SXin Li void * arg;
1375*d0c94b83SXin Li
1376*d0c94b83SXin Li if (!pcm_is_ready(pcm))
1377*d0c94b83SXin Li return -1;
1378*d0c94b83SXin Li
1379*d0c94b83SXin Li va_start(ap, request);
1380*d0c94b83SXin Li arg = va_arg(ap, void *);
1381*d0c94b83SXin Li va_end(ap);
1382*d0c94b83SXin Li
1383*d0c94b83SXin Li return pcm->ops->ioctl(pcm->data, request, arg);
1384*d0c94b83SXin Li }
1385*d0c94b83SXin Li
pcm_get_xruns(struct pcm * pcm)1386*d0c94b83SXin Li int pcm_get_xruns(struct pcm *pcm) {
1387*d0c94b83SXin Li return pcm->xruns;
1388*d0c94b83SXin Li }
1389