blob: 4bc86aa030d8dde24b51c7091de033d3ceb2e912 [file] [log] [blame]
Simon Wilson79d39652011-05-25 13:44:23 -07001/* pcm.c
2**
3** Copyright 2011, The Android Open Source Project
4**
5** Redistribution and use in source and binary forms, with or without
6** modification, are permitted provided that the following conditions are met:
7** * Redistributions of source code must retain the above copyright
8** notice, this list of conditions and the following disclaimer.
9** * Redistributions in binary form must reproduce the above copyright
10** notice, this list of conditions and the following disclaimer in the
11** documentation and/or other materials provided with the distribution.
12** * Neither the name of The Android Open Source Project nor the names of
13** its contributors may be used to endorse or promote products derived
14** from this software without specific prior written permission.
15**
16** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26** DAMAGE.
27*/
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <fcntl.h>
32#include <stdarg.h>
33#include <string.h>
34#include <errno.h>
35#include <unistd.h>
Liam Girdwood6be28f12011-10-13 12:59:51 -070036#include <poll.h>
Simon Wilson79d39652011-05-25 13:44:23 -070037
38#include <sys/ioctl.h>
39#include <sys/mman.h>
40#include <sys/time.h>
Dima Krasner696c4482016-03-05 19:50:02 +020041#include <time.h>
Liam Girdwood6be28f12011-10-13 12:59:51 -070042#include <limits.h>
Simon Wilson79d39652011-05-25 13:44:23 -070043
44#include <linux/ioctl.h>
Taylor Holberton4c5a11d2018-11-28 14:29:52 -050045
46#ifndef __force
Simon Wilson79d39652011-05-25 13:44:23 -070047#define __force
Taylor Holberton4c5a11d2018-11-28 14:29:52 -050048#endif
49
50#ifndef __bitwise
Simon Wilson79d39652011-05-25 13:44:23 -070051#define __bitwise
Taylor Holberton4c5a11d2018-11-28 14:29:52 -050052#endif
53
54#ifndef __user
Simon Wilson79d39652011-05-25 13:44:23 -070055#define __user
Taylor Holberton4c5a11d2018-11-28 14:29:52 -050056#endif
57
Simon Wilson79d39652011-05-25 13:44:23 -070058#include <sound/asound.h>
59
Ricardo Biehl Pasquali04952ee2016-10-05 20:32:09 -030060#include <tinyalsa/pcm.h>
Taylor Holbertonea06b972017-04-06 23:14:14 -070061#include <tinyalsa/limits.h>
Simon Wilson79d39652011-05-25 13:44:23 -070062
Taylor Holberton1f741562018-11-28 16:33:24 -050063#ifndef PARAM_MAX
Simon Wilson79d39652011-05-25 13:44:23 -070064#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
Taylor Holberton1f741562018-11-28 16:33:24 -050065#endif /* PARAM_MAX */
66
67#ifndef SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP
Liam Girdwood6be28f12011-10-13 12:59:51 -070068#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
Taylor Holberton1f741562018-11-28 16:33:24 -050069#endif /* SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP */
Simon Wilson79d39652011-05-25 13:44:23 -070070
71static inline int param_is_mask(int p)
72{
73 return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
74 (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
75}
76
77static inline int param_is_interval(int p)
78{
79 return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
80 (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
81}
82
Taylor Holberton2f387d22016-12-01 15:58:16 -080083static inline const struct snd_interval *param_get_interval(const struct snd_pcm_hw_params *p, int n)
84{
Taylor Holberton25976dc2017-04-10 11:46:40 -070085 return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
Taylor Holberton2f387d22016-12-01 15:58:16 -080086}
87
Simon Wilson79d39652011-05-25 13:44:23 -070088static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
89{
90 return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
91}
92
93static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
94{
95 return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
96}
97
98static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
99{
100 if (bit >= SNDRV_MASK_MAX)
101 return;
102 if (param_is_mask(n)) {
103 struct snd_mask *m = param_to_mask(p, n);
104 m->bits[0] = 0;
105 m->bits[1] = 0;
106 m->bits[bit >> 5] |= (1 << (bit & 31));
107 }
108}
109
110static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
111{
112 if (param_is_interval(n)) {
113 struct snd_interval *i = param_to_interval(p, n);
114 i->min = val;
115 }
116}
117
Taylor Holberton2f387d22016-12-01 15:58:16 -0800118static unsigned int param_get_min(const struct snd_pcm_hw_params *p, int n)
Simon Wilson43544882012-10-31 12:52:39 -0700119{
120 if (param_is_interval(n)) {
Taylor Holberton2f387d22016-12-01 15:58:16 -0800121 const struct snd_interval *i = param_get_interval(p, n);
Simon Wilson43544882012-10-31 12:52:39 -0700122 return i->min;
123 }
124 return 0;
125}
126
Taylor Holberton2f387d22016-12-01 15:58:16 -0800127static unsigned int param_get_max(const struct snd_pcm_hw_params *p, int n)
Simon Wilson43544882012-10-31 12:52:39 -0700128{
129 if (param_is_interval(n)) {
Taylor Holberton2f387d22016-12-01 15:58:16 -0800130 const struct snd_interval *i = param_get_interval(p, n);
Simon Wilson43544882012-10-31 12:52:39 -0700131 return i->max;
132 }
133 return 0;
134}
135
Simon Wilson79d39652011-05-25 13:44:23 -0700136static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
137{
138 if (param_is_interval(n)) {
139 struct snd_interval *i = param_to_interval(p, n);
140 i->min = val;
141 i->max = val;
142 i->integer = 1;
143 }
144}
145
Liam Girdwood6be28f12011-10-13 12:59:51 -0700146static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n)
147{
148 if (param_is_interval(n)) {
149 struct snd_interval *i = param_to_interval(p, n);
150 if (i->integer)
151 return i->max;
152 }
153 return 0;
154}
155
Simon Wilson79d39652011-05-25 13:44:23 -0700156static void param_init(struct snd_pcm_hw_params *p)
157{
158 int n;
Simon Wilson98c1f162011-06-07 16:12:32 -0700159
Simon Wilson79d39652011-05-25 13:44:23 -0700160 memset(p, 0, sizeof(*p));
161 for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
162 n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
163 struct snd_mask *m = param_to_mask(p, n);
164 m->bits[0] = ~0;
165 m->bits[1] = ~0;
166 }
167 for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
168 n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
169 struct snd_interval *i = param_to_interval(p, n);
170 i->min = 0;
171 i->max = ~0;
172 }
Simon Wilson43544882012-10-31 12:52:39 -0700173 p->rmask = ~0U;
174 p->cmask = 0;
175 p->info = ~0U;
Simon Wilson79d39652011-05-25 13:44:23 -0700176}
177
Taylor Holberton861da7a2017-04-10 12:05:51 -0700178static unsigned int pcm_format_to_alsa(enum pcm_format format)
179{
180 switch (format) {
181
182 case PCM_FORMAT_S8:
183 return SNDRV_PCM_FORMAT_S8;
184
185 default:
186 case PCM_FORMAT_S16_LE:
187 return SNDRV_PCM_FORMAT_S16_LE;
188 case PCM_FORMAT_S16_BE:
189 return SNDRV_PCM_FORMAT_S16_BE;
190
191 case PCM_FORMAT_S24_LE:
192 return SNDRV_PCM_FORMAT_S24_LE;
193 case PCM_FORMAT_S24_BE:
194 return SNDRV_PCM_FORMAT_S24_BE;
195
196 case PCM_FORMAT_S24_3LE:
197 return SNDRV_PCM_FORMAT_S24_3LE;
198 case PCM_FORMAT_S24_3BE:
199 return SNDRV_PCM_FORMAT_S24_3BE;
200
201 case PCM_FORMAT_S32_LE:
202 return SNDRV_PCM_FORMAT_S32_LE;
203 case PCM_FORMAT_S32_BE:
204 return SNDRV_PCM_FORMAT_S32_BE;
205 };
206}
207
Simon Wilson79d39652011-05-25 13:44:23 -0700208#define PCM_ERROR_MAX 128
209
Taylor Holberton6d58e012016-10-01 18:32:30 -0400210/** A PCM handle.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800211 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400212 */
Simon Wilson79d39652011-05-25 13:44:23 -0700213struct pcm {
Taylor Holberton6d58e012016-10-01 18:32:30 -0400214 /** The PCM's file descriptor */
Simon Wilson79d39652011-05-25 13:44:23 -0700215 int fd;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400216 /** Flags that were passed to @ref pcm_open */
Simon Wilson79d39652011-05-25 13:44:23 -0700217 unsigned int flags;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400218 /** Whether the PCM is running or not */
Simon Wilson79d39652011-05-25 13:44:23 -0700219 int running:1;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400220 /** The number of underruns that have occured */
Simon Wilson79d39652011-05-25 13:44:23 -0700221 int underruns;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400222 /** Size of the buffer */
Simon Wilson79d39652011-05-25 13:44:23 -0700223 unsigned int buffer_size;
Taylor Holberton17a10242016-11-23 13:18:24 -0800224 /** The boundary for ring buffer pointers */
Liam Girdwood6be28f12011-10-13 12:59:51 -0700225 unsigned int boundary;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400226 /** Description of the last error that occured */
Simon Wilson79d39652011-05-25 13:44:23 -0700227 char error[PCM_ERROR_MAX];
Taylor Holberton6d58e012016-10-01 18:32:30 -0400228 /** Configuration that was passed to @ref pcm_open */
Simon Wilson79d39652011-05-25 13:44:23 -0700229 struct pcm_config config;
Eric Laurent40b018e2011-06-18 10:10:23 -0700230 struct snd_pcm_mmap_status *mmap_status;
231 struct snd_pcm_mmap_control *mmap_control;
232 struct snd_pcm_sync_ptr *sync_ptr;
Liam Girdwood6be28f12011-10-13 12:59:51 -0700233 void *mmap_buffer;
234 unsigned int noirq_frames_per_msec;
Taylor Holberton17a10242016-11-23 13:18:24 -0800235 /** The delay of the PCM, in terms of frames */
Hardik T Shah9ecb93f2014-04-10 18:03:52 +0530236 long pcm_delay;
Taylor Holberton17a10242016-11-23 13:18:24 -0800237 /** The subdevice corresponding to the PCM */
David Wagner4cddf192014-04-02 15:12:54 +0200238 unsigned int subdevice;
Simon Wilson79d39652011-05-25 13:44:23 -0700239};
240
Taylor Holberton861da7a2017-04-10 12:05:51 -0700241static int oops(struct pcm *pcm, int e, const char *fmt, ...)
242{
243 va_list ap;
244 int sz;
245
246 va_start(ap, fmt);
247 vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
248 va_end(ap);
249 sz = strlen(pcm->error);
250
251 if (errno)
252 snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
253 ": %s", strerror(e));
254 return -1;
255}
256
Taylor Holberton6d58e012016-10-01 18:32:30 -0400257/** Gets the buffer size of the PCM.
258 * @param pcm A PCM handle.
259 * @return The buffer size of the PCM.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800260 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400261 */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800262unsigned int pcm_get_buffer_size(const struct pcm *pcm)
Simon Wilson79d39652011-05-25 13:44:23 -0700263{
264 return pcm->buffer_size;
265}
266
Taylor Holberton77979a82016-12-01 20:04:04 -0800267/** Gets the channel count of the PCM.
268 * @param pcm A PCM handle.
269 * @return The channel count of the PCM.
270 * @ingroup libtinyalsa-pcm
271 */
272unsigned int pcm_get_channels(const struct pcm *pcm)
273{
Taylor Holberton25976dc2017-04-10 11:46:40 -0700274 return pcm->config.channels;
Taylor Holberton77979a82016-12-01 20:04:04 -0800275}
276
Taylor Holberton08bb5902017-04-10 11:45:44 -0700277/** Gets the PCM configuration.
278 * @param pcm A PCM handle.
279 * @return The PCM configuration.
280 * This function only returns NULL if
281 * @p pcm is NULL.
282 * @ingroup libtinyalsa-pcm
283 * */
284const struct pcm_config * pcm_get_config(const struct pcm *pcm)
285{
286 if (pcm == NULL)
287 return NULL;
288 return &pcm->config;
289}
290
Taylor Holberton77979a82016-12-01 20:04:04 -0800291/** Gets the rate of the PCM.
292 * The rate is given in frames per second.
293 * @param pcm A PCM handle.
294 * @return The rate of the PCM.
295 * @ingroup libtinyalsa-pcm
296 */
297unsigned int pcm_get_rate(const struct pcm *pcm)
298{
Taylor Holberton25976dc2017-04-10 11:46:40 -0700299 return pcm->config.rate;
Taylor Holberton77979a82016-12-01 20:04:04 -0800300}
301
302/** Gets the format of the PCM.
303 * @param pcm A PCM handle.
304 * @return The format of the PCM.
305 * @ingroup libtinyalsa-pcm
306 */
307enum pcm_format pcm_get_format(const struct pcm *pcm)
308{
Taylor Holberton25976dc2017-04-10 11:46:40 -0700309 return pcm->config.format;
Taylor Holberton77979a82016-12-01 20:04:04 -0800310}
311
Taylor Holberton6d58e012016-10-01 18:32:30 -0400312/** Gets the file descriptor of the PCM.
313 * Useful for extending functionality of the PCM when needed.
Taylor Holbertond265c272016-11-23 13:22:56 -0800314 * @param pcm A PCM handle.
Taylor Holberton6d58e012016-10-01 18:32:30 -0400315 * @return The file descriptor of the PCM.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800316 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400317 */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800318int pcm_get_file_descriptor(const struct pcm *pcm)
Taylor Holbertonbb402602016-08-03 10:15:46 -0400319{
320 return pcm->fd;
321}
322
Taylor Holberton6d58e012016-10-01 18:32:30 -0400323/** Gets the error message for the last error that occured.
324 * If no error occured and this function is called, the results are undefined.
325 * @param pcm A PCM handle.
326 * @return The error message of the last error that occured.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800327 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400328 */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800329const char* pcm_get_error(const struct pcm *pcm)
Simon Wilson79d39652011-05-25 13:44:23 -0700330{
331 return pcm->error;
332}
333
Taylor Holberton861da7a2017-04-10 12:05:51 -0700334/** Sets the PCM configuration.
335 * @param pcm A PCM handle.
336 * @param config The configuration to use for the
337 * PCM. This parameter may be NULL, in which case
338 * the default configuration is used.
339 * @returns Zero on success, a negative errno value
340 * on failure.
341 * @ingroup libtinyalsa-pcm
342 * */
343int pcm_set_config(struct pcm *pcm, const struct pcm_config *config)
344{
345 if (pcm == NULL)
346 return -EFAULT;
347 else if (config == NULL) {
348 config = &pcm->config;
349 pcm->config.channels = 2;
350 pcm->config.rate = 48000;
351 pcm->config.period_size = 1024;
352 pcm->config.period_count = 4;
353 pcm->config.format = PCM_FORMAT_S16_LE;
354 pcm->config.start_threshold = config->period_count * config->period_size;
355 pcm->config.stop_threshold = config->period_count * config->period_size;
356 pcm->config.silence_threshold = 0;
357 } else
358 pcm->config = *config;
359
360 struct snd_pcm_hw_params params;
361 param_init(&params);
362 param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
363 pcm_format_to_alsa(config->format));
364 param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
365 SNDRV_PCM_SUBFORMAT_STD);
366 param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
367 param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
368 pcm_format_to_bits(config->format));
369 param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
370 pcm_format_to_bits(config->format) * config->channels);
371 param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
372 config->channels);
373 param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
374 param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
375
376 if (pcm->flags & PCM_NOIRQ) {
377
378 if (!(pcm->flags & PCM_MMAP)) {
379 oops(pcm, -EINVAL, "noirq only currently supported with mmap().");
380 return -EINVAL;
381 }
382
383 params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
384 pcm->noirq_frames_per_msec = config->rate / 1000;
385 }
386
387 if (pcm->flags & PCM_MMAP)
388 param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
389 SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
390 else
391 param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
392 SNDRV_PCM_ACCESS_RW_INTERLEAVED);
393
394 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
395 int errno_copy = errno;
396 oops(pcm, -errno, "cannot set hw params");
397 return -errno_copy;
398 }
399
400 /* get our refined hw_params */
401 pcm->config.period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
402 pcm->config.period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
403 pcm->buffer_size = config->period_count * config->period_size;
404
405 if (pcm->flags & PCM_MMAP) {
406 pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
407 PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
408 if (pcm->mmap_buffer == MAP_FAILED) {
409 int errno_copy = errno;
410 oops(pcm, -errno, "failed to mmap buffer %d bytes\n",
411 pcm_frames_to_bytes(pcm, pcm->buffer_size));
412 return -errno_copy;
413 }
414 }
415
416 struct snd_pcm_sw_params sparams;
417 memset(&sparams, 0, sizeof(sparams));
418 sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
419 sparams.period_step = 1;
420 sparams.avail_min = 1;
421
422 if (!config->start_threshold) {
423 if (pcm->flags & PCM_IN)
424 pcm->config.start_threshold = sparams.start_threshold = 1;
425 else
426 pcm->config.start_threshold = sparams.start_threshold =
427 config->period_count * config->period_size / 2;
428 } else
429 sparams.start_threshold = config->start_threshold;
430
431 /* pick a high stop threshold - todo: does this need further tuning */
432 if (!config->stop_threshold) {
433 if (pcm->flags & PCM_IN)
434 pcm->config.stop_threshold = sparams.stop_threshold =
435 config->period_count * config->period_size * 10;
436 else
437 pcm->config.stop_threshold = sparams.stop_threshold =
438 config->period_count * config->period_size;
439 }
440 else
441 sparams.stop_threshold = config->stop_threshold;
442
443 sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
444 sparams.silence_size = 0;
445 sparams.silence_threshold = config->silence_threshold;
446 pcm->boundary = sparams.boundary = pcm->buffer_size;
447
448 while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
449 pcm->boundary *= 2;
450
451 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
452 int errno_copy = errno;
453 oops(pcm, -errno, "cannot set sw params");
454 return -errno_copy;
455 }
456
457 return 0;
458}
459
Taylor Holberton6d58e012016-10-01 18:32:30 -0400460/** Gets the subdevice on which the pcm has been opened.
461 * @param pcm A PCM handle.
462 * @return The subdevice on which the pcm has been opened */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800463unsigned int pcm_get_subdevice(const struct pcm *pcm)
David Wagner4cddf192014-04-02 15:12:54 +0200464{
465 return pcm->subdevice;
466}
467
Taylor Holberton6d58e012016-10-01 18:32:30 -0400468/** Determines the number of bits occupied by a @ref pcm_format.
469 * @param format A PCM format.
470 * @return The number of bits associated with @p format
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800471 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400472 */
Simon Wilson7136cf72013-07-17 10:30:35 -0700473unsigned int pcm_format_to_bits(enum pcm_format format)
Simon Wilsonbc03b622011-06-15 17:19:01 -0700474{
475 switch (format) {
476 case PCM_FORMAT_S32_LE:
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400477 case PCM_FORMAT_S32_BE:
Simon Wilson7136cf72013-07-17 10:30:35 -0700478 case PCM_FORMAT_S24_LE:
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400479 case PCM_FORMAT_S24_BE:
Simon Wilsonbc03b622011-06-15 17:19:01 -0700480 return 32;
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400481 case PCM_FORMAT_S24_3LE:
482 case PCM_FORMAT_S24_3BE:
483 return 24;
Simon Wilsonbc03b622011-06-15 17:19:01 -0700484 default:
485 case PCM_FORMAT_S16_LE:
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400486 case PCM_FORMAT_S16_BE:
Simon Wilsonbc03b622011-06-15 17:19:01 -0700487 return 16;
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400488 case PCM_FORMAT_S8:
489 return 8;
Simon Wilsonbc03b622011-06-15 17:19:01 -0700490 };
491}
492
Taylor Holberton6d58e012016-10-01 18:32:30 -0400493/** Determines how many frames of a PCM can fit into a number of bytes.
494 * @param pcm A PCM handle.
495 * @param bytes The number of bytes.
496 * @return The number of frames that may fit into @p bytes
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800497 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400498 */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800499unsigned int pcm_bytes_to_frames(const struct pcm *pcm, unsigned int bytes)
Liam Girdwood6be28f12011-10-13 12:59:51 -0700500{
501 return bytes / (pcm->config.channels *
502 (pcm_format_to_bits(pcm->config.format) >> 3));
503}
504
Taylor Holberton6d58e012016-10-01 18:32:30 -0400505/** Determines how many bytes are occupied by a number of frames of a PCM.
506 * @param pcm A PCM handle.
507 * @param frames The number of frames of a PCM.
508 * @return The bytes occupied by @p frames.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800509 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400510 */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800511unsigned int pcm_frames_to_bytes(const struct pcm *pcm, unsigned int frames)
Liam Girdwood6be28f12011-10-13 12:59:51 -0700512{
513 return frames * pcm->config.channels *
514 (pcm_format_to_bits(pcm->config.format) >> 3);
515}
516
Taylor Holberton4f556062016-09-16 09:54:36 -0400517static int pcm_sync_ptr(struct pcm *pcm, int flags)
518{
Ricardo Biehl Pasqualic1446752018-12-18 23:13:05 -0200519 if (pcm->sync_ptr == NULL) {
520 /* status and control are mmaped */
521
522 if (flags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
523 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HWSYNC) == -1) {
524 oops(pcm, errno, "failed to sync hardware pointer");
525 return -1;
526 }
527 }
528 } else {
Eric Laurent40b018e2011-06-18 10:10:23 -0700529 pcm->sync_ptr->flags = flags;
Taylor Holbertone123a652017-01-13 21:39:48 -0800530 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0) {
531 oops(pcm, errno, "failed to sync mmap ptr");
Eric Laurent40b018e2011-06-18 10:10:23 -0700532 return -1;
Taylor Holbertone123a652017-01-13 21:39:48 -0800533 }
Eric Laurent40b018e2011-06-18 10:10:23 -0700534 }
Ricardo Biehl Pasqualic1446752018-12-18 23:13:05 -0200535
Miguel Gaioc9f97da2018-07-17 10:05:54 +0200536 return 0;
Eric Laurent40b018e2011-06-18 10:10:23 -0700537}
538
Taylor Holberton4f556062016-09-16 09:54:36 -0400539static int pcm_hw_mmap_status(struct pcm *pcm)
540{
Eric Laurent40b018e2011-06-18 10:10:23 -0700541 if (pcm->sync_ptr)
542 return 0;
543
544 int page_size = sysconf(_SC_PAGE_SIZE);
545 pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
546 pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
547 if (pcm->mmap_status == MAP_FAILED)
548 pcm->mmap_status = NULL;
549 if (!pcm->mmap_status)
550 goto mmap_error;
551
552 pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
553 MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
554 if (pcm->mmap_control == MAP_FAILED)
555 pcm->mmap_control = NULL;
556 if (!pcm->mmap_control) {
557 munmap(pcm->mmap_status, page_size);
558 pcm->mmap_status = NULL;
559 goto mmap_error;
560 }
Eric Laurent40b018e2011-06-18 10:10:23 -0700561
562 return 0;
563
564mmap_error:
565
566 pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr));
567 if (!pcm->sync_ptr)
568 return -ENOMEM;
569 pcm->mmap_status = &pcm->sync_ptr->s.status;
570 pcm->mmap_control = &pcm->sync_ptr->c.control;
Eric Laurent40b018e2011-06-18 10:10:23 -0700571
572 return 0;
573}
574
575static void pcm_hw_munmap_status(struct pcm *pcm) {
576 if (pcm->sync_ptr) {
577 free(pcm->sync_ptr);
578 pcm->sync_ptr = NULL;
579 } else {
580 int page_size = sysconf(_SC_PAGE_SIZE);
581 if (pcm->mmap_status)
582 munmap(pcm->mmap_status, page_size);
583 if (pcm->mmap_control)
584 munmap(pcm->mmap_control, page_size);
585 }
586 pcm->mmap_status = NULL;
587 pcm->mmap_control = NULL;
588}
589
Liam Girdwood6be28f12011-10-13 12:59:51 -0700590static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
Eric Laurentbb7c5df2013-09-16 14:31:17 -0700591 char *buf, unsigned int src_offset,
Liam Girdwood6be28f12011-10-13 12:59:51 -0700592 unsigned int frames)
593{
594 int size_bytes = pcm_frames_to_bytes(pcm, frames);
595 int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset);
596 int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
597
598 /* interleaved only atm */
Eric Laurentbb7c5df2013-09-16 14:31:17 -0700599 if (pcm->flags & PCM_IN)
600 memcpy(buf + src_offset_bytes,
601 (char*)pcm->mmap_buffer + pcm_offset_bytes,
602 size_bytes);
603 else
604 memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
605 buf + src_offset_bytes,
606 size_bytes);
Liam Girdwood6be28f12011-10-13 12:59:51 -0700607 return 0;
608}
609
Eric Laurentbb7c5df2013-09-16 14:31:17 -0700610static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf,
Liam Girdwood6be28f12011-10-13 12:59:51 -0700611 unsigned int offset, unsigned int size)
612{
613 void *pcm_areas;
614 int commit;
615 unsigned int pcm_offset, frames, count = 0;
616
617 while (size > 0) {
618 frames = size;
619 pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
Eric Laurentbb7c5df2013-09-16 14:31:17 -0700620 pcm_areas_copy(pcm, pcm_offset, buf, offset, frames);
Liam Girdwood6be28f12011-10-13 12:59:51 -0700621 commit = pcm_mmap_commit(pcm, pcm_offset, frames);
622 if (commit < 0) {
623 oops(pcm, commit, "failed to commit %d frames\n", frames);
624 return commit;
625 }
626
627 offset += commit;
628 count += commit;
629 size -= commit;
630 }
631 return count;
632}
633
Taylor Holberton6d58e012016-10-01 18:32:30 -0400634/** Returns available frames in pcm buffer and corresponding time stamp.
635 * The clock is CLOCK_MONOTONIC if flag @ref PCM_MONOTONIC was specified in @ref pcm_open,
636 * otherwise the clock is CLOCK_REALTIME.
637 * For an input stream, frames available are frames ready for the application to read.
638 * For an output stream, frames available are the number of empty frames available for the application to write.
639 * Only available for PCMs opened with the @ref PCM_MMAP flag.
640 * @param pcm A PCM handle.
641 * @param avail The number of available frames
642 * @param tstamp The timestamp
643 * @return On success, zero is returned; on failure, negative one.
644 */
Eric Laurent40b018e2011-06-18 10:10:23 -0700645int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
646 struct timespec *tstamp)
647{
648 int frames;
649 int rc;
650 snd_pcm_uframes_t hw_ptr;
651
652 if (!pcm_is_ready(pcm))
653 return -1;
654
655 rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
656 if (rc < 0)
657 return -1;
658
Eric Laurent7db48582011-11-17 11:47:59 -0800659 if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
660 (pcm->mmap_status->state != PCM_STATE_DRAINING))
Eric Laurentee9ba872011-11-15 19:04:03 -0800661 return -1;
662
Eric Laurent40b018e2011-06-18 10:10:23 -0700663 *tstamp = pcm->mmap_status->tstamp;
664 if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
665 return -1;
666
667 hw_ptr = pcm->mmap_status->hw_ptr;
668 if (pcm->flags & PCM_IN)
669 frames = hw_ptr - pcm->mmap_control->appl_ptr;
670 else
671 frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
672
673 if (frames < 0)
674 return -1;
675
676 *avail = (unsigned int)frames;
677
678 return 0;
679}
680
Taylor Holberton6d58e012016-10-01 18:32:30 -0400681/** Writes audio samples to PCM.
682 * If the PCM has not been started, it is started in this function.
683 * This function is only valid for PCMs opened with the @ref PCM_OUT flag.
684 * This function is not valid for PCMs opened with the @ref PCM_MMAP flag.
685 * @param pcm A PCM handle.
686 * @param data The audio sample array
Taylor Holbertond7b140a2016-12-01 20:43:28 -0800687 * @param frame_count The number of frames occupied by the sample array.
Taylor Holberton851dd802017-04-06 23:12:01 -0700688 * This value should not be greater than @ref TINYALSA_FRAMES_MAX
689 * or INT_MAX.
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800690 * @return On success, this function returns the number of frames written; otherwise, a negative number.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800691 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400692 */
Taylor Holbertond7b140a2016-12-01 20:43:28 -0800693int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count)
Simon Wilson79d39652011-05-25 13:44:23 -0700694{
695 struct snd_xferi x;
696
697 if (pcm->flags & PCM_IN)
698 return -EINVAL;
Taylor Holberton001b25e2017-04-10 12:42:06 -0700699#if UINT_MAX > TINYALSA_FRAMES_MAX
700 if (frame_count > TINYALSA_FRAMES_MAX)
701 return -EINVAL;
702#endif
703 if (frame_count > INT_MAX)
Taylor Holberton851dd802017-04-06 23:12:01 -0700704 return -EINVAL;
Simon Wilson79d39652011-05-25 13:44:23 -0700705
Mark Brown6bbe77a2012-02-10 22:07:09 +0000706 x.buf = (void*)data;
Taylor Holbertond7b140a2016-12-01 20:43:28 -0800707 x.frames = frame_count;
Taylor Holberton2386a422016-11-18 20:38:40 -0800708 x.result = 0;
Simon Wilson79d39652011-05-25 13:44:23 -0700709 for (;;) {
Simon Wilson79d39652011-05-25 13:44:23 -0700710 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
711 pcm->running = 0;
712 if (errno == EPIPE) {
John Grossmanb6db70a2012-04-03 16:37:38 -0700713 /* we failed to make our window -- try to restart if we are
714 * allowed to do so. Otherwise, simply allow the EPIPE error to
715 * propagate up to the app level */
Simon Wilson79d39652011-05-25 13:44:23 -0700716 pcm->underruns++;
John Grossmanb6db70a2012-04-03 16:37:38 -0700717 if (pcm->flags & PCM_NORESTART)
718 return -EPIPE;
Ricardo Biehl Pasquali7c40a9e2018-09-09 21:40:01 -0300719 if (pcm_prepare(pcm))
720 return -EPIPE;
Simon Wilson79d39652011-05-25 13:44:23 -0700721 continue;
722 }
723 return oops(pcm, errno, "cannot write stream data");
724 }
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800725 return x.result;
Simon Wilson79d39652011-05-25 13:44:23 -0700726 }
727}
728
Taylor Holberton6d58e012016-10-01 18:32:30 -0400729/** Reads audio samples from PCM.
730 * If the PCM has not been started, it is started in this function.
731 * This function is only valid for PCMs opened with the @ref PCM_IN flag.
732 * This function is not valid for PCMs opened with the @ref PCM_MMAP flag.
733 * @param pcm A PCM handle.
734 * @param data The audio sample array
Taylor Holbertond1c98e42016-12-01 21:21:49 -0800735 * @param frame_count The number of frames occupied by the sample array.
Taylor Holberton851dd802017-04-06 23:12:01 -0700736 * This value should not be greater than @ref TINYALSA_FRAMES_MAX
737 * or INT_MAX.
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800738 * @return On success, this function returns the number of frames written; otherwise, a negative number.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800739 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400740 */
Taylor Holbertond7b140a2016-12-01 20:43:28 -0800741int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count)
Simon Wilson79d39652011-05-25 13:44:23 -0700742{
743 struct snd_xferi x;
744
745 if (!(pcm->flags & PCM_IN))
746 return -EINVAL;
Taylor Holberton001b25e2017-04-10 12:42:06 -0700747#if UINT_MAX > TINYALSA_FRAMES_MAX
748 if (frame_count > TINYALSA_FRAMES_MAX)
749 return -EINVAL;
750#endif
751 if (frame_count > INT_MAX)
Taylor Holberton851dd802017-04-06 23:12:01 -0700752 return -EINVAL;
Simon Wilson79d39652011-05-25 13:44:23 -0700753
754 x.buf = data;
Taylor Holbertond7b140a2016-12-01 20:43:28 -0800755 x.frames = frame_count;
Taylor Holberton2386a422016-11-18 20:38:40 -0800756 x.result = 0;
Simon Wilson79d39652011-05-25 13:44:23 -0700757 for (;;) {
Ricardo Biehl Pasquali13e11fe2018-08-21 14:42:23 -0300758 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
Simon Wilson79d39652011-05-25 13:44:23 -0700759 pcm->running = 0;
760 if (errno == EPIPE) {
761 /* we failed to make our window -- try to restart */
762 pcm->underruns++;
Ricardo Biehl Pasqualif85cf622019-01-07 19:33:13 -0200763 if (pcm->flags & PCM_NORESTART)
764 return -EPIPE;
765 if (pcm_prepare(pcm))
766 return -EPIPE;
Simon Wilson79d39652011-05-25 13:44:23 -0700767 continue;
768 }
769 return oops(pcm, errno, "cannot read stream data");
770 }
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800771 return x.result;
Simon Wilson79d39652011-05-25 13:44:23 -0700772 }
773}
774
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800775/** Writes audio samples to PCM.
776 * If the PCM has not been started, it is started in this function.
777 * This function is only valid for PCMs opened with the @ref PCM_OUT flag.
778 * This function is not valid for PCMs opened with the @ref PCM_MMAP flag.
779 * @param pcm A PCM handle.
780 * @param data The audio sample array
781 * @param count The number of bytes occupied by the sample array.
782 * @return On success, this function returns zero; otherwise, a negative number.
783 * @deprecated
784 * @ingroup libtinyalsa-pcm
785 */
786int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
787{
Taylor Holbertonea06b972017-04-06 23:14:14 -0700788 return pcm_writei(pcm, data, pcm_bytes_to_frames(pcm, count));
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800789}
790
791/** Reads audio samples from PCM.
792 * If the PCM has not been started, it is started in this function.
793 * This function is only valid for PCMs opened with the @ref PCM_IN flag.
794 * This function is not valid for PCMs opened with the @ref PCM_MMAP flag.
795 * @param pcm A PCM handle.
796 * @param data The audio sample array
797 * @param count The number of bytes occupied by the sample array.
798 * @return On success, this function returns zero; otherwise, a negative number.
799 * @deprecated
800 * @ingroup libtinyalsa-pcm
801 */
802int pcm_read(struct pcm *pcm, void *data, unsigned int count)
803{
Taylor Holbertonea06b972017-04-06 23:14:14 -0700804 return pcm_readi(pcm, data, pcm_bytes_to_frames(pcm, count));
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800805}
806
Simon Wilson79d39652011-05-25 13:44:23 -0700807static struct pcm bad_pcm = {
808 .fd = -1,
809};
810
Taylor Holberton6d58e012016-10-01 18:32:30 -0400811/** Gets the hardware parameters of a PCM, without created a PCM handle.
812 * @param card The card of the PCM.
813 * The default card is zero.
814 * @param device The device of the PCM.
815 * The default device is zero.
816 * @param flags Specifies whether the PCM is an input or output.
817 * May be one of the following:
818 * - @ref PCM_IN
819 * - @ref PCM_OUT
820 * @return On success, the hardware parameters of the PCM; on failure, NULL.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800821 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400822 */
Simon Wilson43544882012-10-31 12:52:39 -0700823struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
824 unsigned int flags)
825{
826 struct snd_pcm_hw_params *params;
827 char fn[256];
828 int fd;
829
830 snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
831 flags & PCM_IN ? 'c' : 'p');
832
Taylor Holberton093b8782017-10-12 20:28:30 -0400833 if (flags & PCM_NONBLOCK)
834 fd = open(fn, O_RDWR | O_NONBLOCK);
835 else
836 fd = open(fn, O_RDWR);
837
Simon Wilson43544882012-10-31 12:52:39 -0700838 if (fd < 0) {
Taylor Holberton093b8782017-10-12 20:28:30 -0400839 fprintf(stderr, "cannot open device '%s': %s\n", fn, strerror(errno));
Simon Wilson43544882012-10-31 12:52:39 -0700840 goto err_open;
841 }
842
843 params = calloc(1, sizeof(struct snd_pcm_hw_params));
844 if (!params)
845 goto err_calloc;
846
847 param_init(params);
848 if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
849 fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
850 goto err_hw_refine;
851 }
852
853 close(fd);
854
855 return (struct pcm_params *)params;
856
857err_hw_refine:
858 free(params);
859err_calloc:
860 close(fd);
861err_open:
862 return NULL;
863}
864
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500865/** Frees the hardware parameters returned by @ref pcm_params_get.
Taylor Holberton6d58e012016-10-01 18:32:30 -0400866 * @param pcm_params Hardware parameters of a PCM.
867 * May be NULL.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800868 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400869 */
Simon Wilson43544882012-10-31 12:52:39 -0700870void pcm_params_free(struct pcm_params *pcm_params)
871{
872 struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
873
874 if (params)
875 free(params);
876}
877
878static int pcm_param_to_alsa(enum pcm_param param)
879{
880 switch (param) {
Andy Hungad807622014-03-10 18:08:15 -0700881 case PCM_PARAM_ACCESS:
882 return SNDRV_PCM_HW_PARAM_ACCESS;
883 case PCM_PARAM_FORMAT:
884 return SNDRV_PCM_HW_PARAM_FORMAT;
885 case PCM_PARAM_SUBFORMAT:
886 return SNDRV_PCM_HW_PARAM_SUBFORMAT;
Simon Wilson43544882012-10-31 12:52:39 -0700887 case PCM_PARAM_SAMPLE_BITS:
888 return SNDRV_PCM_HW_PARAM_SAMPLE_BITS;
889 break;
890 case PCM_PARAM_FRAME_BITS:
891 return SNDRV_PCM_HW_PARAM_FRAME_BITS;
892 break;
893 case PCM_PARAM_CHANNELS:
894 return SNDRV_PCM_HW_PARAM_CHANNELS;
895 break;
896 case PCM_PARAM_RATE:
897 return SNDRV_PCM_HW_PARAM_RATE;
898 break;
899 case PCM_PARAM_PERIOD_TIME:
900 return SNDRV_PCM_HW_PARAM_PERIOD_TIME;
901 break;
902 case PCM_PARAM_PERIOD_SIZE:
903 return SNDRV_PCM_HW_PARAM_PERIOD_SIZE;
904 break;
905 case PCM_PARAM_PERIOD_BYTES:
906 return SNDRV_PCM_HW_PARAM_PERIOD_BYTES;
907 break;
908 case PCM_PARAM_PERIODS:
909 return SNDRV_PCM_HW_PARAM_PERIODS;
910 break;
911 case PCM_PARAM_BUFFER_TIME:
912 return SNDRV_PCM_HW_PARAM_BUFFER_TIME;
913 break;
914 case PCM_PARAM_BUFFER_SIZE:
915 return SNDRV_PCM_HW_PARAM_BUFFER_SIZE;
916 break;
917 case PCM_PARAM_BUFFER_BYTES:
918 return SNDRV_PCM_HW_PARAM_BUFFER_BYTES;
919 break;
920 case PCM_PARAM_TICK_TIME:
921 return SNDRV_PCM_HW_PARAM_TICK_TIME;
922 break;
923
924 default:
925 return -1;
926 }
927}
928
Taylor Holberton6d58e012016-10-01 18:32:30 -0400929/** Gets a mask from a PCM's hardware parameters.
930 * @param pcm_params A PCM's hardware parameters.
931 * @param param The parameter to get.
932 * @return If @p pcm_params is NULL or @p param is not a mask, NULL is returned.
933 * Otherwise, the mask associated with @p param is returned.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800934 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400935 */
Taylor Holberton2f387d22016-12-01 15:58:16 -0800936const struct pcm_mask *pcm_params_get_mask(const struct pcm_params *pcm_params,
Andy Hungad807622014-03-10 18:08:15 -0700937 enum pcm_param param)
938{
939 int p;
940 struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
941 if (params == NULL) {
942 return NULL;
943 }
944
945 p = pcm_param_to_alsa(param);
946 if (p < 0 || !param_is_mask(p)) {
947 return NULL;
948 }
949
Taylor Holberton2f387d22016-12-01 15:58:16 -0800950 return (const struct pcm_mask *)param_to_mask(params, p);
Andy Hungad807622014-03-10 18:08:15 -0700951}
952
Taylor Holberton17a10242016-11-23 13:18:24 -0800953/** Get the minimum of a specified PCM parameter.
954 * @param pcm_params A PCM parameters structure.
955 * @param param The specified parameter to get the minimum of.
956 * @returns On success, the parameter minimum.
957 * On failure, zero.
958 */
Taylor Holberton2f387d22016-12-01 15:58:16 -0800959unsigned int pcm_params_get_min(const struct pcm_params *pcm_params,
Simon Wilson43544882012-10-31 12:52:39 -0700960 enum pcm_param param)
961{
962 struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
963 int p;
964
965 if (!params)
966 return 0;
967
968 p = pcm_param_to_alsa(param);
969 if (p < 0)
970 return 0;
971
972 return param_get_min(params, p);
973}
974
Taylor Holberton17a10242016-11-23 13:18:24 -0800975/** Get the maximum of a specified PCM parameter.
976 * @param pcm_params A PCM parameters structure.
977 * @param param The specified parameter to get the maximum of.
978 * @returns On success, the parameter maximum.
979 * On failure, zero.
980 */
Taylor Holberton2f387d22016-12-01 15:58:16 -0800981unsigned int pcm_params_get_max(const struct pcm_params *pcm_params,
Simon Wilson43544882012-10-31 12:52:39 -0700982 enum pcm_param param)
983{
Taylor Holberton2f387d22016-12-01 15:58:16 -0800984 const struct snd_pcm_hw_params *params = (const struct snd_pcm_hw_params *)pcm_params;
Simon Wilson43544882012-10-31 12:52:39 -0700985 int p;
986
987 if (!params)
988 return 0;
989
990 p = pcm_param_to_alsa(param);
991 if (p < 0)
992 return 0;
993
994 return param_get_max(params, p);
995}
996
Taylor Holberton6d58e012016-10-01 18:32:30 -0400997/** Closes a PCM returned by @ref pcm_open.
998 * @param pcm A PCM returned by @ref pcm_open.
999 * May not be NULL.
1000 * @return Always returns zero.
Taylor Holberton8e1b1022016-11-19 10:34:50 -08001001 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -04001002 */
Simon Wilson79d39652011-05-25 13:44:23 -07001003int pcm_close(struct pcm *pcm)
1004{
1005 if (pcm == &bad_pcm)
1006 return 0;
1007
Eric Laurent40b018e2011-06-18 10:10:23 -07001008 pcm_hw_munmap_status(pcm);
1009
Liam Girdwood6be28f12011-10-13 12:59:51 -07001010 if (pcm->flags & PCM_MMAP) {
1011 pcm_stop(pcm);
1012 munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
1013 }
1014
Simon Wilson79d39652011-05-25 13:44:23 -07001015 if (pcm->fd >= 0)
1016 close(pcm->fd);
1017 pcm->running = 0;
1018 pcm->buffer_size = 0;
1019 pcm->fd = -1;
Eric Laurent40b018e2011-06-18 10:10:23 -07001020 free(pcm);
Simon Wilson79d39652011-05-25 13:44:23 -07001021 return 0;
1022}
1023
Taylor Holbertonc6f908e2016-12-24 20:33:33 -08001024/** Opens a PCM by it's name.
1025 * @param name The name of the PCM.
1026 * The name is given in the format: <i>hw</i>:<b>card</b>,<b>device</b>
1027 * @param flags Specify characteristics and functionality about the pcm.
1028 * May be a bitwise AND of the following:
1029 * - @ref PCM_IN
1030 * - @ref PCM_OUT
1031 * - @ref PCM_MMAP
1032 * - @ref PCM_NOIRQ
1033 * - @ref PCM_MONOTONIC
1034 * @param config The hardware and software parameters to open the PCM with.
Taylor Holbertone123a652017-01-13 21:39:48 -08001035 * @returns A PCM structure.
1036 * If an error occurs allocating memory for the PCM, NULL is returned.
1037 * Otherwise, client code should check that the PCM opened properly by calling @ref pcm_is_ready.
1038 * If @ref pcm_is_ready, check @ref pcm_get_error for more information.
Taylor Holbertonc6f908e2016-12-24 20:33:33 -08001039 * @ingroup libtinyalsa-pcm
1040 */
1041struct pcm *pcm_open_by_name(const char *name,
1042 unsigned int flags,
1043 const struct pcm_config *config)
1044{
1045 unsigned int card, device;
1046 if ((name[0] != 'h')
1047 || (name[1] != 'w')
1048 || (name[2] != ':')) {
1049 return NULL;
1050 } else if (sscanf(&name[3], "%u,%u", &card, &device) != 2) {
1051 return NULL;
1052 }
1053 return pcm_open(card, device, flags, config);
1054}
1055
Taylor Holberton6d58e012016-10-01 18:32:30 -04001056/** Opens a PCM.
1057 * @param card The card that the pcm belongs to.
1058 * The default card is zero.
1059 * @param device The device that the pcm belongs to.
1060 * The default device is zero.
1061 * @param flags Specify characteristics and functionality about the pcm.
1062 * May be a bitwise AND of the following:
1063 * - @ref PCM_IN
1064 * - @ref PCM_OUT
1065 * - @ref PCM_MMAP
1066 * - @ref PCM_NOIRQ
1067 * - @ref PCM_MONOTONIC
1068 * @param config The hardware and software parameters to open the PCM with.
Taylor Holbertone123a652017-01-13 21:39:48 -08001069 * @returns A PCM structure.
1070 * If an error occurs allocating memory for the PCM, NULL is returned.
1071 * Otherwise, client code should check that the PCM opened properly by calling @ref pcm_is_ready.
1072 * If @ref pcm_is_ready, check @ref pcm_get_error for more information.
Taylor Holberton8e1b1022016-11-19 10:34:50 -08001073 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -04001074 */
Simon Wilson1bd580f2011-06-02 15:58:41 -07001075struct pcm *pcm_open(unsigned int card, unsigned int device,
Taylor Holberton94803b02016-12-01 16:07:14 -08001076 unsigned int flags, const struct pcm_config *config)
Simon Wilson79d39652011-05-25 13:44:23 -07001077{
Simon Wilson79d39652011-05-25 13:44:23 -07001078 struct pcm *pcm;
1079 struct snd_pcm_info info;
Simon Wilson1bd580f2011-06-02 15:58:41 -07001080 char fn[256];
Eric Laurent40b018e2011-06-18 10:10:23 -07001081 int rc;
Simon Wilson79d39652011-05-25 13:44:23 -07001082
1083 pcm = calloc(1, sizeof(struct pcm));
Taylor Holbertonf319eb02016-10-14 20:05:30 -04001084 if (!pcm)
1085 return &bad_pcm;
Simon Wilson79d39652011-05-25 13:44:23 -07001086
Simon Wilson1bd580f2011-06-02 15:58:41 -07001087 snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
1088 flags & PCM_IN ? 'c' : 'p');
Simon Wilson79d39652011-05-25 13:44:23 -07001089
1090 pcm->flags = flags;
Taylor Holberton093b8782017-10-12 20:28:30 -04001091
1092 if (flags & PCM_NONBLOCK)
1093 pcm->fd = open(fn, O_RDWR | O_NONBLOCK);
1094 else
1095 pcm->fd = open(fn, O_RDWR);
1096
Simon Wilson79d39652011-05-25 13:44:23 -07001097 if (pcm->fd < 0) {
Simon Wilson1bd580f2011-06-02 15:58:41 -07001098 oops(pcm, errno, "cannot open device '%s'", fn);
Simon Wilson79d39652011-05-25 13:44:23 -07001099 return pcm;
1100 }
1101
1102 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
Simon Wilson851aa5c2011-05-30 21:18:26 -07001103 oops(pcm, errno, "cannot get info");
Liam Girdwood6be28f12011-10-13 12:59:51 -07001104 goto fail_close;
Simon Wilson79d39652011-05-25 13:44:23 -07001105 }
David Wagner4cddf192014-04-02 15:12:54 +02001106 pcm->subdevice = info.subdevice;
Simon Wilson79d39652011-05-25 13:44:23 -07001107
Taylor Holberton861da7a2017-04-10 12:05:51 -07001108 if (pcm_set_config(pcm, config) != 0)
Liam Girdwood6be28f12011-10-13 12:59:51 -07001109 goto fail_close;
Simon Wilson79d39652011-05-25 13:44:23 -07001110
Eric Laurent40b018e2011-06-18 10:10:23 -07001111 rc = pcm_hw_mmap_status(pcm);
1112 if (rc < 0) {
1113 oops(pcm, rc, "mmap status failed");
1114 goto fail;
1115 }
1116
Glenn Kasten81012402013-08-22 15:11:48 -07001117#ifdef SNDRV_PCM_IOCTL_TTSTAMP
1118 if (pcm->flags & PCM_MONOTONIC) {
1119 int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
1120 rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
1121 if (rc < 0) {
1122 oops(pcm, rc, "cannot set timestamp type");
1123 goto fail;
1124 }
1125 }
1126#endif
1127
Ricardo Biehl Pasquali13bf6292018-08-22 16:10:45 -03001128 /* prepare here so the user does not need to do this later */
1129 if (pcm_prepare(pcm))
1130 goto fail;
1131
Simon Wilson79d39652011-05-25 13:44:23 -07001132 pcm->underruns = 0;
1133 return pcm;
1134
1135fail:
Liam Girdwood6be28f12011-10-13 12:59:51 -07001136 if (flags & PCM_MMAP)
1137 munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
1138fail_close:
Simon Wilson79d39652011-05-25 13:44:23 -07001139 close(pcm->fd);
1140 pcm->fd = -1;
1141 return pcm;
1142}
1143
Taylor Holberton6d58e012016-10-01 18:32:30 -04001144/** Checks if a PCM file has been opened without error.
1145 * @param pcm A PCM handle.
Taylor Holbertone123a652017-01-13 21:39:48 -08001146 * May be NULL.
1147 * @return If a PCM's file descriptor is not valid or the pointer is NULL, it returns zero.
Taylor Holberton6d58e012016-10-01 18:32:30 -04001148 * Otherwise, the function returns one.
Taylor Holberton8e1b1022016-11-19 10:34:50 -08001149 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -04001150 */
Taylor Holberton15d58482016-12-01 17:46:29 -08001151int pcm_is_ready(const struct pcm *pcm)
Simon Wilson79d39652011-05-25 13:44:23 -07001152{
Taylor Holbertone123a652017-01-13 21:39:48 -08001153 if (pcm != NULL) {
1154 return pcm->fd >= 0;
1155 }
1156 return 0;
Simon Wilson79d39652011-05-25 13:44:23 -07001157}
Simon Wilsond6458e62011-06-21 14:58:11 -07001158
Taylor Holberton558e5942016-12-04 13:42:28 -08001159/** Links two PCMs.
1160 * After this function is called, the two PCMs will prepare, start and stop in sync (at the same time).
1161 * If an error occurs, the error message will be written to @p pcm1.
1162 * @param pcm1 A PCM handle.
1163 * @param pcm2 Another PCM handle.
1164 * @return On success, zero; on failure, a negative number.
1165 * @ingroup libtinyalsa-pcm
1166 */
1167int pcm_link(struct pcm *pcm1, struct pcm *pcm2)
1168{
1169 int err = ioctl(pcm1->fd, SNDRV_PCM_IOCTL_LINK, pcm2->fd);
1170 if (err == -1) {
1171 return oops(pcm1, errno, "cannot link PCM");
1172 }
1173 return 0;
1174}
1175
1176/** Unlinks a PCM.
1177 * @see @ref pcm_link
1178 * @param pcm A PCM handle.
1179 * @return On success, zero; on failure, a negative number.
1180 * @ingroup libtinyalsa-pcm
1181 */
1182int pcm_unlink(struct pcm *pcm)
1183{
1184 int err = ioctl(pcm->fd, SNDRV_PCM_IOCTL_UNLINK);
1185 if (err == -1) {
1186 return oops(pcm, errno, "cannot unlink PCM");
1187 }
1188 return 0;
1189}
1190
Taylor Holberton6d58e012016-10-01 18:32:30 -04001191/** Prepares a PCM, if it has not been prepared already.
1192 * @param pcm A PCM handle.
1193 * @return On success, zero; on failure, a negative number.
Taylor Holberton8e1b1022016-11-19 10:34:50 -08001194 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -04001195 */
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +05301196int pcm_prepare(struct pcm *pcm)
Simon Wilsond6458e62011-06-21 14:58:11 -07001197{
1198 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
1199 return oops(pcm, errno, "cannot prepare channel");
Liam Girdwood6be28f12011-10-13 12:59:51 -07001200
Ricardo Biehl Pasquali535a6ba2018-12-30 11:53:54 -02001201 /* get appl_ptr and avail_min from kernel */
1202 pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
1203
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +05301204 return 0;
1205}
1206
Taylor Holberton6d58e012016-10-01 18:32:30 -04001207/** Starts a PCM.
Taylor Holberton6d58e012016-10-01 18:32:30 -04001208 * @param pcm A PCM handle.
1209 * @return On success, zero; on failure, a negative number.
Taylor Holberton8e1b1022016-11-19 10:34:50 -08001210 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -04001211 */
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +05301212int pcm_start(struct pcm *pcm)
1213{
Ricardo Biehl Pasquali7ff9cde2018-12-31 17:23:39 -02001214 /* set appl_ptr and avail_min in kernel */
Miguel Gaiocf5f0632018-07-17 13:30:24 +02001215 pcm_sync_ptr(pcm, 0);
Liam Girdwood6be28f12011-10-13 12:59:51 -07001216
Miguel Gaiocf5f0632018-07-17 13:30:24 +02001217 if (pcm->mmap_status->state != PCM_STATE_RUNNING) {
1218 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
1219 return oops(pcm, errno, "cannot start channel");
1220 }
Simon Wilsond6458e62011-06-21 14:58:11 -07001221
Liam Girdwood6be28f12011-10-13 12:59:51 -07001222 pcm->running = 1;
Simon Wilsond6458e62011-06-21 14:58:11 -07001223 return 0;
1224}
1225
Taylor Holberton6d58e012016-10-01 18:32:30 -04001226/** Stops a PCM.
1227 * @param pcm A PCM handle.
1228 * @return On success, zero; on failure, a negative number.
Taylor Holberton8e1b1022016-11-19 10:34:50 -08001229 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -04001230 */
Simon Wilsond6458e62011-06-21 14:58:11 -07001231int pcm_stop(struct pcm *pcm)
1232{
1233 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
1234 return oops(pcm, errno, "cannot stop channel");
1235
Liam Girdwood6be28f12011-10-13 12:59:51 -07001236 pcm->running = 0;
Simon Wilsond6458e62011-06-21 14:58:11 -07001237 return 0;
1238}
1239
Liam Girdwood6be28f12011-10-13 12:59:51 -07001240static inline int pcm_mmap_playback_avail(struct pcm *pcm)
1241{
1242 int avail;
1243
1244 avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
1245
1246 if (avail < 0)
1247 avail += pcm->boundary;
StevenNANb0fc3e92014-03-17 11:14:49 +08001248 else if (avail >= (int)pcm->boundary)
Liam Girdwood6be28f12011-10-13 12:59:51 -07001249 avail -= pcm->boundary;
1250
1251 return avail;
1252}
1253
1254static inline int pcm_mmap_capture_avail(struct pcm *pcm)
1255{
1256 int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr;
1257 if (avail < 0)
1258 avail += pcm->boundary;
1259 return avail;
1260}
1261
1262static inline int pcm_mmap_avail(struct pcm *pcm)
1263{
Liam Girdwood6be28f12011-10-13 12:59:51 -07001264 if (pcm->flags & PCM_IN)
1265 return pcm_mmap_capture_avail(pcm);
1266 else
1267 return pcm_mmap_playback_avail(pcm);
1268}
1269
1270static void pcm_mmap_appl_forward(struct pcm *pcm, int frames)
1271{
1272 unsigned int appl_ptr = pcm->mmap_control->appl_ptr;
1273 appl_ptr += frames;
1274
1275 /* check for boundary wrap */
1276 if (appl_ptr > pcm->boundary)
1277 appl_ptr -= pcm->boundary;
1278 pcm->mmap_control->appl_ptr = appl_ptr;
1279}
1280
1281int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
1282 unsigned int *frames)
1283{
1284 unsigned int continuous, copy_frames, avail;
1285
1286 /* return the mmap buffer */
1287 *areas = pcm->mmap_buffer;
1288
1289 /* and the application offset in frames */
1290 *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size;
1291
1292 avail = pcm_mmap_avail(pcm);
1293 if (avail > pcm->buffer_size)
1294 avail = pcm->buffer_size;
1295 continuous = pcm->buffer_size - *offset;
1296
1297 /* we can only copy frames if the are availabale and continuos */
1298 copy_frames = *frames;
1299 if (copy_frames > avail)
1300 copy_frames = avail;
1301 if (copy_frames > continuous)
1302 copy_frames = continuous;
1303 *frames = copy_frames;
1304
1305 return 0;
1306}
1307
1308int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames)
1309{
Taylor Holberton72e44222016-11-22 09:54:47 -08001310 int ret;
1311
Taylor Holberton73466c02016-10-01 12:51:59 -04001312 /* not used */
1313 (void) offset;
1314
Liam Girdwood6be28f12011-10-13 12:59:51 -07001315 /* update the application pointer in userspace and kernel */
1316 pcm_mmap_appl_forward(pcm, frames);
Taylor Holberton72e44222016-11-22 09:54:47 -08001317 ret = pcm_sync_ptr(pcm, 0);
Taylor Holbertone123a652017-01-13 21:39:48 -08001318 if (ret != 0){
1319 printf("%d\n", ret);
Taylor Holberton72e44222016-11-22 09:54:47 -08001320 return ret;
Taylor Holbertone123a652017-01-13 21:39:48 -08001321 }
Liam Girdwood6be28f12011-10-13 12:59:51 -07001322
1323 return frames;
1324}
1325
1326int pcm_avail_update(struct pcm *pcm)
1327{
1328 pcm_sync_ptr(pcm, 0);
1329 return pcm_mmap_avail(pcm);
1330}
1331
1332int pcm_state(struct pcm *pcm)
1333{
1334 int err = pcm_sync_ptr(pcm, 0);
1335 if (err < 0)
1336 return err;
1337
1338 return pcm->mmap_status->state;
1339}
1340
Taylor Holberton17a10242016-11-23 13:18:24 -08001341/** Waits for frames to be available for read or write operations.
1342 * @param pcm A PCM handle.
1343 * @param timeout The maximum amount of time to wait for, in terms of milliseconds.
1344 * @returns If frames became available, one is returned.
1345 * If a timeout occured, zero is returned.
1346 * If an error occured, a negative number is returned.
1347 * @ingroup libtinyalsa-pcm
1348 */
Liam Girdwood6be28f12011-10-13 12:59:51 -07001349int pcm_wait(struct pcm *pcm, int timeout)
1350{
1351 struct pollfd pfd;
Liam Girdwood6be28f12011-10-13 12:59:51 -07001352 int err;
1353
1354 pfd.fd = pcm->fd;
Apelete Seketeli84889d02014-02-14 14:34:32 +01001355 pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
Liam Girdwood6be28f12011-10-13 12:59:51 -07001356
1357 do {
1358 /* let's wait for avail or timeout */
1359 err = poll(&pfd, 1, timeout);
1360 if (err < 0)
1361 return -errno;
1362
1363 /* timeout ? */
1364 if (err == 0)
1365 return 0;
1366
1367 /* have we been interrupted ? */
1368 if (errno == -EINTR)
1369 continue;
1370
1371 /* check for any errors */
1372 if (pfd.revents & (POLLERR | POLLNVAL)) {
1373 switch (pcm_state(pcm)) {
1374 case PCM_STATE_XRUN:
1375 return -EPIPE;
1376 case PCM_STATE_SUSPENDED:
1377 return -ESTRPIPE;
1378 case PCM_STATE_DISCONNECTED:
1379 return -ENODEV;
1380 default:
1381 return -EIO;
1382 }
1383 }
1384 /* poll again if fd not ready for IO */
1385 } while (!(pfd.revents & (POLLIN | POLLOUT)));
1386
1387 return 1;
1388}
1389
Eric Laurentbb7c5df2013-09-16 14:31:17 -07001390int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes)
Liam Girdwood6be28f12011-10-13 12:59:51 -07001391{
1392 int err = 0, frames, avail;
1393 unsigned int offset = 0, count;
1394
1395 if (bytes == 0)
1396 return 0;
1397
1398 count = pcm_bytes_to_frames(pcm, bytes);
1399
1400 while (count > 0) {
1401
1402 /* get the available space for writing new frames */
1403 avail = pcm_avail_update(pcm);
1404 if (avail < 0) {
1405 fprintf(stderr, "cannot determine available mmap frames");
1406 return err;
1407 }
1408
1409 /* start the audio if we reach the threshold */
Taylor Holberton25976dc2017-04-10 11:46:40 -07001410 if (!pcm->running &&
Liam Girdwood6be28f12011-10-13 12:59:51 -07001411 (pcm->buffer_size - avail) >= pcm->config.start_threshold) {
1412 if (pcm_start(pcm) < 0) {
1413 fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n",
1414 (unsigned int)pcm->mmap_status->hw_ptr,
1415 (unsigned int)pcm->mmap_control->appl_ptr,
1416 avail);
1417 return -errno;
1418 }
1419 }
1420
1421 /* sleep until we have space to write new frames */
1422 if (pcm->running &&
1423 (unsigned int)avail < pcm->mmap_control->avail_min) {
1424 int time = -1;
1425
1426 if (pcm->flags & PCM_NOIRQ)
1427 time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min)
1428 / pcm->noirq_frames_per_msec;
1429
1430 err = pcm_wait(pcm, time);
1431 if (err < 0) {
1432 pcm->running = 0;
1433 fprintf(stderr, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
1434 (unsigned int)pcm->mmap_status->hw_ptr,
1435 (unsigned int)pcm->mmap_control->appl_ptr,
1436 avail);
1437 pcm->mmap_control->appl_ptr = 0;
1438 return err;
1439 }
1440 continue;
1441 }
1442
1443 frames = count;
1444 if (frames > avail)
1445 frames = avail;
1446
1447 if (!frames)
1448 break;
1449
1450 /* copy frames from buffer */
Eric Laurentbb7c5df2013-09-16 14:31:17 -07001451 frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames);
Liam Girdwood6be28f12011-10-13 12:59:51 -07001452 if (frames < 0) {
1453 fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
1454 (unsigned int)pcm->mmap_status->hw_ptr,
1455 (unsigned int)pcm->mmap_control->appl_ptr,
1456 avail);
1457 return frames;
1458 }
1459
1460 offset += frames;
1461 count -= frames;
1462 }
1463
Liam Girdwood6be28f12011-10-13 12:59:51 -07001464 return 0;
1465}
Eric Laurentbb7c5df2013-09-16 14:31:17 -07001466
1467int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count)
1468{
1469 if ((~pcm->flags) & (PCM_OUT | PCM_MMAP))
1470 return -ENOSYS;
1471
1472 return pcm_mmap_transfer(pcm, (void *)data, count);
1473}
1474
1475int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count)
1476{
1477 if ((~pcm->flags) & (PCM_IN | PCM_MMAP))
1478 return -ENOSYS;
1479
1480 return pcm_mmap_transfer(pcm, data, count);
1481}
Hardik T Shah9ecb93f2014-04-10 18:03:52 +05301482
Taylor Holberton17a10242016-11-23 13:18:24 -08001483/** Gets the delay of the PCM, in terms of frames.
1484 * @param pcm A PCM handle.
1485 * @returns On success, the delay of the PCM.
1486 * On failure, a negative number.
1487 * @ingroup libtinyalsa-pcm
1488 */
Hardik T Shah9ecb93f2014-04-10 18:03:52 +05301489long pcm_get_delay(struct pcm *pcm)
1490{
1491 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DELAY, &pcm->pcm_delay) < 0)
1492 return -1;
1493
1494 return pcm->pcm_delay;
1495}
Taylor Holberton6d58e012016-10-01 18:32:30 -04001496