blob: b4bbda1c608a3494f06e3930ab54e9db5941a6f8 [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>
45#define __force
46#define __bitwise
47#define __user
48#include <sound/asound.h>
49
Ricardo Biehl Pasquali04952ee2016-10-05 20:32:09 -030050#include <tinyalsa/pcm.h>
Taylor Holbertonea06b972017-04-06 23:14:14 -070051#include <tinyalsa/limits.h>
Simon Wilson79d39652011-05-25 13:44:23 -070052
53#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
Liam Girdwood6be28f12011-10-13 12:59:51 -070054#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
Simon Wilson79d39652011-05-25 13:44:23 -070055
56static inline int param_is_mask(int p)
57{
58 return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
59 (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
60}
61
62static inline int param_is_interval(int p)
63{
64 return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
65 (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
66}
67
Taylor Holberton2f387d22016-12-01 15:58:16 -080068static inline const struct snd_interval *param_get_interval(const struct snd_pcm_hw_params *p, int n)
69{
70 return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
71}
72
Simon Wilson79d39652011-05-25 13:44:23 -070073static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
74{
75 return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
76}
77
78static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
79{
80 return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
81}
82
83static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
84{
85 if (bit >= SNDRV_MASK_MAX)
86 return;
87 if (param_is_mask(n)) {
88 struct snd_mask *m = param_to_mask(p, n);
89 m->bits[0] = 0;
90 m->bits[1] = 0;
91 m->bits[bit >> 5] |= (1 << (bit & 31));
92 }
93}
94
95static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
96{
97 if (param_is_interval(n)) {
98 struct snd_interval *i = param_to_interval(p, n);
99 i->min = val;
100 }
101}
102
Taylor Holberton2f387d22016-12-01 15:58:16 -0800103static unsigned int param_get_min(const struct snd_pcm_hw_params *p, int n)
Simon Wilson43544882012-10-31 12:52:39 -0700104{
105 if (param_is_interval(n)) {
Taylor Holberton2f387d22016-12-01 15:58:16 -0800106 const struct snd_interval *i = param_get_interval(p, n);
Simon Wilson43544882012-10-31 12:52:39 -0700107 return i->min;
108 }
109 return 0;
110}
111
Taylor Holberton2f387d22016-12-01 15:58:16 -0800112static unsigned int param_get_max(const struct snd_pcm_hw_params *p, int n)
Simon Wilson43544882012-10-31 12:52:39 -0700113{
114 if (param_is_interval(n)) {
Taylor Holberton2f387d22016-12-01 15:58:16 -0800115 const struct snd_interval *i = param_get_interval(p, n);
Simon Wilson43544882012-10-31 12:52:39 -0700116 return i->max;
117 }
118 return 0;
119}
120
Simon Wilson79d39652011-05-25 13:44:23 -0700121static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
122{
123 if (param_is_interval(n)) {
124 struct snd_interval *i = param_to_interval(p, n);
125 i->min = val;
126 i->max = val;
127 i->integer = 1;
128 }
129}
130
Liam Girdwood6be28f12011-10-13 12:59:51 -0700131static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n)
132{
133 if (param_is_interval(n)) {
134 struct snd_interval *i = param_to_interval(p, n);
135 if (i->integer)
136 return i->max;
137 }
138 return 0;
139}
140
Simon Wilson79d39652011-05-25 13:44:23 -0700141static void param_init(struct snd_pcm_hw_params *p)
142{
143 int n;
Simon Wilson98c1f162011-06-07 16:12:32 -0700144
Simon Wilson79d39652011-05-25 13:44:23 -0700145 memset(p, 0, sizeof(*p));
146 for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
147 n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
148 struct snd_mask *m = param_to_mask(p, n);
149 m->bits[0] = ~0;
150 m->bits[1] = ~0;
151 }
152 for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
153 n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
154 struct snd_interval *i = param_to_interval(p, n);
155 i->min = 0;
156 i->max = ~0;
157 }
Simon Wilson43544882012-10-31 12:52:39 -0700158 p->rmask = ~0U;
159 p->cmask = 0;
160 p->info = ~0U;
Simon Wilson79d39652011-05-25 13:44:23 -0700161}
162
163#define PCM_ERROR_MAX 128
164
Taylor Holberton6d58e012016-10-01 18:32:30 -0400165/** A PCM handle.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800166 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400167 */
Simon Wilson79d39652011-05-25 13:44:23 -0700168struct pcm {
Taylor Holberton6d58e012016-10-01 18:32:30 -0400169 /** The PCM's file descriptor */
Simon Wilson79d39652011-05-25 13:44:23 -0700170 int fd;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400171 /** Flags that were passed to @ref pcm_open */
Simon Wilson79d39652011-05-25 13:44:23 -0700172 unsigned int flags;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400173 /** Whether the PCM is running or not */
Simon Wilson79d39652011-05-25 13:44:23 -0700174 int running:1;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400175 /** Whether or not the PCM has been prepared */
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +0530176 int prepared:1;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400177 /** The number of underruns that have occured */
Simon Wilson79d39652011-05-25 13:44:23 -0700178 int underruns;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400179 /** Size of the buffer */
Simon Wilson79d39652011-05-25 13:44:23 -0700180 unsigned int buffer_size;
Taylor Holberton17a10242016-11-23 13:18:24 -0800181 /** The boundary for ring buffer pointers */
Liam Girdwood6be28f12011-10-13 12:59:51 -0700182 unsigned int boundary;
Taylor Holberton6d58e012016-10-01 18:32:30 -0400183 /** Description of the last error that occured */
Simon Wilson79d39652011-05-25 13:44:23 -0700184 char error[PCM_ERROR_MAX];
Taylor Holberton6d58e012016-10-01 18:32:30 -0400185 /** Configuration that was passed to @ref pcm_open */
Simon Wilson79d39652011-05-25 13:44:23 -0700186 struct pcm_config config;
Eric Laurent40b018e2011-06-18 10:10:23 -0700187 struct snd_pcm_mmap_status *mmap_status;
188 struct snd_pcm_mmap_control *mmap_control;
189 struct snd_pcm_sync_ptr *sync_ptr;
Liam Girdwood6be28f12011-10-13 12:59:51 -0700190 void *mmap_buffer;
191 unsigned int noirq_frames_per_msec;
Taylor Holberton17a10242016-11-23 13:18:24 -0800192 /** The delay of the PCM, in terms of frames */
Hardik T Shah9ecb93f2014-04-10 18:03:52 +0530193 long pcm_delay;
Taylor Holberton17a10242016-11-23 13:18:24 -0800194 /** The subdevice corresponding to the PCM */
David Wagner4cddf192014-04-02 15:12:54 +0200195 unsigned int subdevice;
Simon Wilson79d39652011-05-25 13:44:23 -0700196};
197
Taylor Holberton6d58e012016-10-01 18:32:30 -0400198/** Gets the buffer size of the PCM.
199 * @param pcm A PCM handle.
200 * @return The buffer size of the PCM.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800201 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400202 */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800203unsigned int pcm_get_buffer_size(const struct pcm *pcm)
Simon Wilson79d39652011-05-25 13:44:23 -0700204{
205 return pcm->buffer_size;
206}
207
Taylor Holberton77979a82016-12-01 20:04:04 -0800208/** Gets the channel count of the PCM.
209 * @param pcm A PCM handle.
210 * @return The channel count of the PCM.
211 * @ingroup libtinyalsa-pcm
212 */
213unsigned int pcm_get_channels(const struct pcm *pcm)
214{
215 return pcm->config.channels;
216}
217
218/** Gets the rate of the PCM.
219 * The rate is given in frames per second.
220 * @param pcm A PCM handle.
221 * @return The rate of the PCM.
222 * @ingroup libtinyalsa-pcm
223 */
224unsigned int pcm_get_rate(const struct pcm *pcm)
225{
226 return pcm->config.rate;
227}
228
229/** Gets the format of the PCM.
230 * @param pcm A PCM handle.
231 * @return The format of the PCM.
232 * @ingroup libtinyalsa-pcm
233 */
234enum pcm_format pcm_get_format(const struct pcm *pcm)
235{
236 return pcm->config.format;
237}
238
Taylor Holberton6d58e012016-10-01 18:32:30 -0400239/** Gets the file descriptor of the PCM.
240 * Useful for extending functionality of the PCM when needed.
Taylor Holbertond265c272016-11-23 13:22:56 -0800241 * @param pcm A PCM handle.
Taylor Holberton6d58e012016-10-01 18:32:30 -0400242 * @return The file descriptor of the PCM.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800243 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400244 */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800245int pcm_get_file_descriptor(const struct pcm *pcm)
Taylor Holbertonbb402602016-08-03 10:15:46 -0400246{
247 return pcm->fd;
248}
249
Taylor Holberton6d58e012016-10-01 18:32:30 -0400250/** Gets the error message for the last error that occured.
251 * If no error occured and this function is called, the results are undefined.
252 * @param pcm A PCM handle.
253 * @return The error message of the last error that occured.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800254 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400255 */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800256const char* pcm_get_error(const struct pcm *pcm)
Simon Wilson79d39652011-05-25 13:44:23 -0700257{
258 return pcm->error;
259}
260
Taylor Holberton6d58e012016-10-01 18:32:30 -0400261/** Gets the subdevice on which the pcm has been opened.
262 * @param pcm A PCM handle.
263 * @return The subdevice on which the pcm has been opened */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800264unsigned int pcm_get_subdevice(const struct pcm *pcm)
David Wagner4cddf192014-04-02 15:12:54 +0200265{
266 return pcm->subdevice;
267}
268
Simon Wilson79d39652011-05-25 13:44:23 -0700269static int oops(struct pcm *pcm, int e, const char *fmt, ...)
270{
271 va_list ap;
272 int sz;
273
274 va_start(ap, fmt);
275 vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
276 va_end(ap);
277 sz = strlen(pcm->error);
278
279 if (errno)
280 snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
281 ": %s", strerror(e));
282 return -1;
283}
284
Simon Wilsonbc03b622011-06-15 17:19:01 -0700285static unsigned int pcm_format_to_alsa(enum pcm_format format)
286{
287 switch (format) {
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400288
Gabriel M. Beddingfield2a274a12012-05-02 11:51:20 -0500289 case PCM_FORMAT_S8:
290 return SNDRV_PCM_FORMAT_S8;
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400291
Simon Wilsonbc03b622011-06-15 17:19:01 -0700292 default:
293 case PCM_FORMAT_S16_LE:
294 return SNDRV_PCM_FORMAT_S16_LE;
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400295 case PCM_FORMAT_S16_BE:
296 return SNDRV_PCM_FORMAT_S16_BE;
297
298 case PCM_FORMAT_S24_LE:
299 return SNDRV_PCM_FORMAT_S24_LE;
300 case PCM_FORMAT_S24_BE:
301 return SNDRV_PCM_FORMAT_S24_BE;
302
303 case PCM_FORMAT_S24_3LE:
304 return SNDRV_PCM_FORMAT_S24_3LE;
305 case PCM_FORMAT_S24_3BE:
306 return SNDRV_PCM_FORMAT_S24_3BE;
307
308 case PCM_FORMAT_S32_LE:
309 return SNDRV_PCM_FORMAT_S32_LE;
310 case PCM_FORMAT_S32_BE:
311 return SNDRV_PCM_FORMAT_S32_BE;
Simon Wilsonbc03b622011-06-15 17:19:01 -0700312 };
313}
314
Taylor Holberton6d58e012016-10-01 18:32:30 -0400315/** Determines the number of bits occupied by a @ref pcm_format.
316 * @param format A PCM format.
317 * @return The number of bits associated with @p format
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800318 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400319 */
Simon Wilson7136cf72013-07-17 10:30:35 -0700320unsigned int pcm_format_to_bits(enum pcm_format format)
Simon Wilsonbc03b622011-06-15 17:19:01 -0700321{
322 switch (format) {
323 case PCM_FORMAT_S32_LE:
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400324 case PCM_FORMAT_S32_BE:
Simon Wilson7136cf72013-07-17 10:30:35 -0700325 case PCM_FORMAT_S24_LE:
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400326 case PCM_FORMAT_S24_BE:
Simon Wilsonbc03b622011-06-15 17:19:01 -0700327 return 32;
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400328 case PCM_FORMAT_S24_3LE:
329 case PCM_FORMAT_S24_3BE:
330 return 24;
Simon Wilsonbc03b622011-06-15 17:19:01 -0700331 default:
332 case PCM_FORMAT_S16_LE:
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400333 case PCM_FORMAT_S16_BE:
Simon Wilsonbc03b622011-06-15 17:19:01 -0700334 return 16;
Taylor Holbertonc01d4a32016-10-01 12:22:43 -0400335 case PCM_FORMAT_S8:
336 return 8;
Simon Wilsonbc03b622011-06-15 17:19:01 -0700337 };
338}
339
Taylor Holberton6d58e012016-10-01 18:32:30 -0400340/** Determines how many frames of a PCM can fit into a number of bytes.
341 * @param pcm A PCM handle.
342 * @param bytes The number of bytes.
343 * @return The number of frames that may fit into @p bytes
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800344 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400345 */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800346unsigned int pcm_bytes_to_frames(const struct pcm *pcm, unsigned int bytes)
Liam Girdwood6be28f12011-10-13 12:59:51 -0700347{
348 return bytes / (pcm->config.channels *
349 (pcm_format_to_bits(pcm->config.format) >> 3));
350}
351
Taylor Holberton6d58e012016-10-01 18:32:30 -0400352/** Determines how many bytes are occupied by a number of frames of a PCM.
353 * @param pcm A PCM handle.
354 * @param frames The number of frames of a PCM.
355 * @return The bytes occupied by @p frames.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800356 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400357 */
Taylor Holberton147d7ad2016-12-01 17:50:31 -0800358unsigned int pcm_frames_to_bytes(const struct pcm *pcm, unsigned int frames)
Liam Girdwood6be28f12011-10-13 12:59:51 -0700359{
360 return frames * pcm->config.channels *
361 (pcm_format_to_bits(pcm->config.format) >> 3);
362}
363
Taylor Holberton4f556062016-09-16 09:54:36 -0400364static int pcm_sync_ptr(struct pcm *pcm, int flags)
365{
Eric Laurent40b018e2011-06-18 10:10:23 -0700366 if (pcm->sync_ptr) {
367 pcm->sync_ptr->flags = flags;
Taylor Holbertone123a652017-01-13 21:39:48 -0800368 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0) {
369 oops(pcm, errno, "failed to sync mmap ptr");
Eric Laurent40b018e2011-06-18 10:10:23 -0700370 return -1;
Taylor Holbertone123a652017-01-13 21:39:48 -0800371 }
Taylor Holberton72e44222016-11-22 09:54:47 -0800372 return 0;
Eric Laurent40b018e2011-06-18 10:10:23 -0700373 }
Taylor Holberton72e44222016-11-22 09:54:47 -0800374 return -1;
Eric Laurent40b018e2011-06-18 10:10:23 -0700375}
376
Taylor Holberton4f556062016-09-16 09:54:36 -0400377static int pcm_hw_mmap_status(struct pcm *pcm)
378{
Eric Laurent40b018e2011-06-18 10:10:23 -0700379 if (pcm->sync_ptr)
380 return 0;
381
382 int page_size = sysconf(_SC_PAGE_SIZE);
383 pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
384 pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
385 if (pcm->mmap_status == MAP_FAILED)
386 pcm->mmap_status = NULL;
387 if (!pcm->mmap_status)
388 goto mmap_error;
389
390 pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
391 MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
392 if (pcm->mmap_control == MAP_FAILED)
393 pcm->mmap_control = NULL;
394 if (!pcm->mmap_control) {
395 munmap(pcm->mmap_status, page_size);
396 pcm->mmap_status = NULL;
397 goto mmap_error;
398 }
399 pcm->mmap_control->avail_min = 1;
400
401 return 0;
402
403mmap_error:
404
405 pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr));
406 if (!pcm->sync_ptr)
407 return -ENOMEM;
408 pcm->mmap_status = &pcm->sync_ptr->s.status;
409 pcm->mmap_control = &pcm->sync_ptr->c.control;
410 pcm->mmap_control->avail_min = 1;
411 pcm_sync_ptr(pcm, 0);
412
413 return 0;
414}
415
416static void pcm_hw_munmap_status(struct pcm *pcm) {
417 if (pcm->sync_ptr) {
418 free(pcm->sync_ptr);
419 pcm->sync_ptr = NULL;
420 } else {
421 int page_size = sysconf(_SC_PAGE_SIZE);
422 if (pcm->mmap_status)
423 munmap(pcm->mmap_status, page_size);
424 if (pcm->mmap_control)
425 munmap(pcm->mmap_control, page_size);
426 }
427 pcm->mmap_status = NULL;
428 pcm->mmap_control = NULL;
429}
430
Liam Girdwood6be28f12011-10-13 12:59:51 -0700431static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
Eric Laurentbb7c5df2013-09-16 14:31:17 -0700432 char *buf, unsigned int src_offset,
Liam Girdwood6be28f12011-10-13 12:59:51 -0700433 unsigned int frames)
434{
435 int size_bytes = pcm_frames_to_bytes(pcm, frames);
436 int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset);
437 int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
438
439 /* interleaved only atm */
Eric Laurentbb7c5df2013-09-16 14:31:17 -0700440 if (pcm->flags & PCM_IN)
441 memcpy(buf + src_offset_bytes,
442 (char*)pcm->mmap_buffer + pcm_offset_bytes,
443 size_bytes);
444 else
445 memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
446 buf + src_offset_bytes,
447 size_bytes);
Liam Girdwood6be28f12011-10-13 12:59:51 -0700448 return 0;
449}
450
Eric Laurentbb7c5df2013-09-16 14:31:17 -0700451static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf,
Liam Girdwood6be28f12011-10-13 12:59:51 -0700452 unsigned int offset, unsigned int size)
453{
454 void *pcm_areas;
455 int commit;
456 unsigned int pcm_offset, frames, count = 0;
457
458 while (size > 0) {
459 frames = size;
460 pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
Eric Laurentbb7c5df2013-09-16 14:31:17 -0700461 pcm_areas_copy(pcm, pcm_offset, buf, offset, frames);
Liam Girdwood6be28f12011-10-13 12:59:51 -0700462 commit = pcm_mmap_commit(pcm, pcm_offset, frames);
463 if (commit < 0) {
464 oops(pcm, commit, "failed to commit %d frames\n", frames);
465 return commit;
466 }
467
468 offset += commit;
469 count += commit;
470 size -= commit;
471 }
472 return count;
473}
474
Taylor Holberton6d58e012016-10-01 18:32:30 -0400475/** Returns available frames in pcm buffer and corresponding time stamp.
476 * The clock is CLOCK_MONOTONIC if flag @ref PCM_MONOTONIC was specified in @ref pcm_open,
477 * otherwise the clock is CLOCK_REALTIME.
478 * For an input stream, frames available are frames ready for the application to read.
479 * For an output stream, frames available are the number of empty frames available for the application to write.
480 * Only available for PCMs opened with the @ref PCM_MMAP flag.
481 * @param pcm A PCM handle.
482 * @param avail The number of available frames
483 * @param tstamp The timestamp
484 * @return On success, zero is returned; on failure, negative one.
485 */
Eric Laurent40b018e2011-06-18 10:10:23 -0700486int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
487 struct timespec *tstamp)
488{
489 int frames;
490 int rc;
491 snd_pcm_uframes_t hw_ptr;
492
493 if (!pcm_is_ready(pcm))
494 return -1;
495
496 rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
497 if (rc < 0)
498 return -1;
499
Eric Laurent7db48582011-11-17 11:47:59 -0800500 if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
501 (pcm->mmap_status->state != PCM_STATE_DRAINING))
Eric Laurentee9ba872011-11-15 19:04:03 -0800502 return -1;
503
Eric Laurent40b018e2011-06-18 10:10:23 -0700504 *tstamp = pcm->mmap_status->tstamp;
505 if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
506 return -1;
507
508 hw_ptr = pcm->mmap_status->hw_ptr;
509 if (pcm->flags & PCM_IN)
510 frames = hw_ptr - pcm->mmap_control->appl_ptr;
511 else
512 frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
513
514 if (frames < 0)
515 return -1;
516
517 *avail = (unsigned int)frames;
518
519 return 0;
520}
521
Taylor Holberton6d58e012016-10-01 18:32:30 -0400522/** Writes audio samples to PCM.
523 * If the PCM has not been started, it is started in this function.
524 * This function is only valid for PCMs opened with the @ref PCM_OUT flag.
525 * This function is not valid for PCMs opened with the @ref PCM_MMAP flag.
526 * @param pcm A PCM handle.
527 * @param data The audio sample array
Taylor Holbertond7b140a2016-12-01 20:43:28 -0800528 * @param frame_count The number of frames occupied by the sample array.
Taylor Holberton851dd802017-04-06 23:12:01 -0700529 * This value should not be greater than @ref TINYALSA_FRAMES_MAX
530 * or INT_MAX.
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800531 * @return On success, this function returns the number of frames written; otherwise, a negative number.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800532 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400533 */
Taylor Holbertond7b140a2016-12-01 20:43:28 -0800534int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count)
Simon Wilson79d39652011-05-25 13:44:23 -0700535{
536 struct snd_xferi x;
537
538 if (pcm->flags & PCM_IN)
539 return -EINVAL;
Taylor Holberton851dd802017-04-06 23:12:01 -0700540 else if ((frame_count > TINYALSA_FRAMES_MAX)
541 || (frame_count > INT_MAX))
542 return -EINVAL;
Simon Wilson79d39652011-05-25 13:44:23 -0700543
Mark Brown6bbe77a2012-02-10 22:07:09 +0000544 x.buf = (void*)data;
Taylor Holbertond7b140a2016-12-01 20:43:28 -0800545 x.frames = frame_count;
Taylor Holberton2386a422016-11-18 20:38:40 -0800546 x.result = 0;
Simon Wilson79d39652011-05-25 13:44:23 -0700547 for (;;) {
548 if (!pcm->running) {
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +0530549 int prepare_error = pcm_prepare(pcm);
550 if (prepare_error)
551 return prepare_error;
Simon Wilson79d39652011-05-25 13:44:23 -0700552 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
553 return oops(pcm, errno, "cannot write initial data");
554 pcm->running = 1;
555 return 0;
556 }
557 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +0530558 pcm->prepared = 0;
Simon Wilson79d39652011-05-25 13:44:23 -0700559 pcm->running = 0;
560 if (errno == EPIPE) {
John Grossmanb6db70a2012-04-03 16:37:38 -0700561 /* we failed to make our window -- try to restart if we are
562 * allowed to do so. Otherwise, simply allow the EPIPE error to
563 * propagate up to the app level */
Simon Wilson79d39652011-05-25 13:44:23 -0700564 pcm->underruns++;
John Grossmanb6db70a2012-04-03 16:37:38 -0700565 if (pcm->flags & PCM_NORESTART)
566 return -EPIPE;
Simon Wilson79d39652011-05-25 13:44:23 -0700567 continue;
568 }
569 return oops(pcm, errno, "cannot write stream data");
570 }
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800571 return x.result;
Simon Wilson79d39652011-05-25 13:44:23 -0700572 }
573}
574
Taylor Holberton6d58e012016-10-01 18:32:30 -0400575/** Reads audio samples from PCM.
576 * If the PCM has not been started, it is started in this function.
577 * This function is only valid for PCMs opened with the @ref PCM_IN flag.
578 * This function is not valid for PCMs opened with the @ref PCM_MMAP flag.
579 * @param pcm A PCM handle.
580 * @param data The audio sample array
Taylor Holbertond1c98e42016-12-01 21:21:49 -0800581 * @param frame_count The number of frames occupied by the sample array.
Taylor Holberton851dd802017-04-06 23:12:01 -0700582 * This value should not be greater than @ref TINYALSA_FRAMES_MAX
583 * or INT_MAX.
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800584 * @return On success, this function returns the number of frames written; otherwise, a negative number.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800585 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400586 */
Taylor Holbertond7b140a2016-12-01 20:43:28 -0800587int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count)
Simon Wilson79d39652011-05-25 13:44:23 -0700588{
589 struct snd_xferi x;
590
591 if (!(pcm->flags & PCM_IN))
592 return -EINVAL;
Taylor Holberton851dd802017-04-06 23:12:01 -0700593 else if ((frame_count > TINYALSA_FRAMES_MAX)
594 || (frame_count > INT_MAX))
595 return -EINVAL;
Simon Wilson79d39652011-05-25 13:44:23 -0700596
597 x.buf = data;
Taylor Holbertond7b140a2016-12-01 20:43:28 -0800598 x.frames = frame_count;
Taylor Holberton2386a422016-11-18 20:38:40 -0800599 x.result = 0;
Simon Wilson79d39652011-05-25 13:44:23 -0700600 for (;;) {
Taylor Holberton1137fc72017-04-06 23:06:36 -0700601 if ((!pcm->running) && (pcm_start(pcm) < 0))
602 return -errno;
603 else if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +0530604 pcm->prepared = 0;
Simon Wilson79d39652011-05-25 13:44:23 -0700605 pcm->running = 0;
606 if (errno == EPIPE) {
607 /* we failed to make our window -- try to restart */
608 pcm->underruns++;
609 continue;
610 }
611 return oops(pcm, errno, "cannot read stream data");
612 }
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800613 return x.result;
Simon Wilson79d39652011-05-25 13:44:23 -0700614 }
615}
616
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800617/** Writes audio samples to PCM.
618 * If the PCM has not been started, it is started in this function.
619 * This function is only valid for PCMs opened with the @ref PCM_OUT flag.
620 * This function is not valid for PCMs opened with the @ref PCM_MMAP flag.
621 * @param pcm A PCM handle.
622 * @param data The audio sample array
623 * @param count The number of bytes occupied by the sample array.
624 * @return On success, this function returns zero; otherwise, a negative number.
625 * @deprecated
626 * @ingroup libtinyalsa-pcm
627 */
628int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
629{
Taylor Holbertonea06b972017-04-06 23:14:14 -0700630 return pcm_writei(pcm, data, pcm_bytes_to_frames(pcm, count));
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800631}
632
633/** Reads audio samples from PCM.
634 * If the PCM has not been started, it is started in this function.
635 * This function is only valid for PCMs opened with the @ref PCM_IN flag.
636 * This function is not valid for PCMs opened with the @ref PCM_MMAP flag.
637 * @param pcm A PCM handle.
638 * @param data The audio sample array
639 * @param count The number of bytes occupied by the sample array.
640 * @return On success, this function returns zero; otherwise, a negative number.
641 * @deprecated
642 * @ingroup libtinyalsa-pcm
643 */
644int pcm_read(struct pcm *pcm, void *data, unsigned int count)
645{
Taylor Holbertonea06b972017-04-06 23:14:14 -0700646 return pcm_readi(pcm, data, pcm_bytes_to_frames(pcm, count));
Taylor Holbertonf9834ee2016-12-01 20:25:41 -0800647}
648
Simon Wilson79d39652011-05-25 13:44:23 -0700649static struct pcm bad_pcm = {
650 .fd = -1,
651};
652
Taylor Holberton6d58e012016-10-01 18:32:30 -0400653/** Gets the hardware parameters of a PCM, without created a PCM handle.
654 * @param card The card of the PCM.
655 * The default card is zero.
656 * @param device The device of the PCM.
657 * The default device is zero.
658 * @param flags Specifies whether the PCM is an input or output.
659 * May be one of the following:
660 * - @ref PCM_IN
661 * - @ref PCM_OUT
662 * @return On success, the hardware parameters of the PCM; on failure, NULL.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800663 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400664 */
Simon Wilson43544882012-10-31 12:52:39 -0700665struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
666 unsigned int flags)
667{
668 struct snd_pcm_hw_params *params;
669 char fn[256];
670 int fd;
671
672 snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
673 flags & PCM_IN ? 'c' : 'p');
674
675 fd = open(fn, O_RDWR);
676 if (fd < 0) {
677 fprintf(stderr, "cannot open device '%s'\n", fn);
678 goto err_open;
679 }
680
681 params = calloc(1, sizeof(struct snd_pcm_hw_params));
682 if (!params)
683 goto err_calloc;
684
685 param_init(params);
686 if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
687 fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
688 goto err_hw_refine;
689 }
690
691 close(fd);
692
693 return (struct pcm_params *)params;
694
695err_hw_refine:
696 free(params);
697err_calloc:
698 close(fd);
699err_open:
700 return NULL;
701}
702
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500703/** Frees the hardware parameters returned by @ref pcm_params_get.
Taylor Holberton6d58e012016-10-01 18:32:30 -0400704 * @param pcm_params Hardware parameters of a PCM.
705 * May be NULL.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800706 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400707 */
Simon Wilson43544882012-10-31 12:52:39 -0700708void pcm_params_free(struct pcm_params *pcm_params)
709{
710 struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
711
712 if (params)
713 free(params);
714}
715
716static int pcm_param_to_alsa(enum pcm_param param)
717{
718 switch (param) {
Andy Hungad807622014-03-10 18:08:15 -0700719 case PCM_PARAM_ACCESS:
720 return SNDRV_PCM_HW_PARAM_ACCESS;
721 case PCM_PARAM_FORMAT:
722 return SNDRV_PCM_HW_PARAM_FORMAT;
723 case PCM_PARAM_SUBFORMAT:
724 return SNDRV_PCM_HW_PARAM_SUBFORMAT;
Simon Wilson43544882012-10-31 12:52:39 -0700725 case PCM_PARAM_SAMPLE_BITS:
726 return SNDRV_PCM_HW_PARAM_SAMPLE_BITS;
727 break;
728 case PCM_PARAM_FRAME_BITS:
729 return SNDRV_PCM_HW_PARAM_FRAME_BITS;
730 break;
731 case PCM_PARAM_CHANNELS:
732 return SNDRV_PCM_HW_PARAM_CHANNELS;
733 break;
734 case PCM_PARAM_RATE:
735 return SNDRV_PCM_HW_PARAM_RATE;
736 break;
737 case PCM_PARAM_PERIOD_TIME:
738 return SNDRV_PCM_HW_PARAM_PERIOD_TIME;
739 break;
740 case PCM_PARAM_PERIOD_SIZE:
741 return SNDRV_PCM_HW_PARAM_PERIOD_SIZE;
742 break;
743 case PCM_PARAM_PERIOD_BYTES:
744 return SNDRV_PCM_HW_PARAM_PERIOD_BYTES;
745 break;
746 case PCM_PARAM_PERIODS:
747 return SNDRV_PCM_HW_PARAM_PERIODS;
748 break;
749 case PCM_PARAM_BUFFER_TIME:
750 return SNDRV_PCM_HW_PARAM_BUFFER_TIME;
751 break;
752 case PCM_PARAM_BUFFER_SIZE:
753 return SNDRV_PCM_HW_PARAM_BUFFER_SIZE;
754 break;
755 case PCM_PARAM_BUFFER_BYTES:
756 return SNDRV_PCM_HW_PARAM_BUFFER_BYTES;
757 break;
758 case PCM_PARAM_TICK_TIME:
759 return SNDRV_PCM_HW_PARAM_TICK_TIME;
760 break;
761
762 default:
763 return -1;
764 }
765}
766
Taylor Holberton6d58e012016-10-01 18:32:30 -0400767/** Gets a mask from a PCM's hardware parameters.
768 * @param pcm_params A PCM's hardware parameters.
769 * @param param The parameter to get.
770 * @return If @p pcm_params is NULL or @p param is not a mask, NULL is returned.
771 * Otherwise, the mask associated with @p param is returned.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800772 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400773 */
Taylor Holberton2f387d22016-12-01 15:58:16 -0800774const struct pcm_mask *pcm_params_get_mask(const struct pcm_params *pcm_params,
Andy Hungad807622014-03-10 18:08:15 -0700775 enum pcm_param param)
776{
777 int p;
778 struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
779 if (params == NULL) {
780 return NULL;
781 }
782
783 p = pcm_param_to_alsa(param);
784 if (p < 0 || !param_is_mask(p)) {
785 return NULL;
786 }
787
Taylor Holberton2f387d22016-12-01 15:58:16 -0800788 return (const struct pcm_mask *)param_to_mask(params, p);
Andy Hungad807622014-03-10 18:08:15 -0700789}
790
Taylor Holberton17a10242016-11-23 13:18:24 -0800791/** Get the minimum of a specified PCM parameter.
792 * @param pcm_params A PCM parameters structure.
793 * @param param The specified parameter to get the minimum of.
794 * @returns On success, the parameter minimum.
795 * On failure, zero.
796 */
Taylor Holberton2f387d22016-12-01 15:58:16 -0800797unsigned int pcm_params_get_min(const struct pcm_params *pcm_params,
Simon Wilson43544882012-10-31 12:52:39 -0700798 enum pcm_param param)
799{
800 struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
801 int p;
802
803 if (!params)
804 return 0;
805
806 p = pcm_param_to_alsa(param);
807 if (p < 0)
808 return 0;
809
810 return param_get_min(params, p);
811}
812
Taylor Holberton17a10242016-11-23 13:18:24 -0800813/** Get the maximum of a specified PCM parameter.
814 * @param pcm_params A PCM parameters structure.
815 * @param param The specified parameter to get the maximum of.
816 * @returns On success, the parameter maximum.
817 * On failure, zero.
818 */
Taylor Holberton2f387d22016-12-01 15:58:16 -0800819unsigned int pcm_params_get_max(const struct pcm_params *pcm_params,
Simon Wilson43544882012-10-31 12:52:39 -0700820 enum pcm_param param)
821{
Taylor Holberton2f387d22016-12-01 15:58:16 -0800822 const struct snd_pcm_hw_params *params = (const struct snd_pcm_hw_params *)pcm_params;
Simon Wilson43544882012-10-31 12:52:39 -0700823 int p;
824
825 if (!params)
826 return 0;
827
828 p = pcm_param_to_alsa(param);
829 if (p < 0)
830 return 0;
831
832 return param_get_max(params, p);
833}
834
Taylor Holberton6d58e012016-10-01 18:32:30 -0400835/** Closes a PCM returned by @ref pcm_open.
836 * @param pcm A PCM returned by @ref pcm_open.
837 * May not be NULL.
838 * @return Always returns zero.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800839 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400840 */
Simon Wilson79d39652011-05-25 13:44:23 -0700841int pcm_close(struct pcm *pcm)
842{
843 if (pcm == &bad_pcm)
844 return 0;
845
Eric Laurent40b018e2011-06-18 10:10:23 -0700846 pcm_hw_munmap_status(pcm);
847
Liam Girdwood6be28f12011-10-13 12:59:51 -0700848 if (pcm->flags & PCM_MMAP) {
849 pcm_stop(pcm);
850 munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
851 }
852
Simon Wilson79d39652011-05-25 13:44:23 -0700853 if (pcm->fd >= 0)
854 close(pcm->fd);
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +0530855 pcm->prepared = 0;
Simon Wilson79d39652011-05-25 13:44:23 -0700856 pcm->running = 0;
857 pcm->buffer_size = 0;
858 pcm->fd = -1;
Eric Laurent40b018e2011-06-18 10:10:23 -0700859 free(pcm);
Simon Wilson79d39652011-05-25 13:44:23 -0700860 return 0;
861}
862
Taylor Holbertonc6f908e2016-12-24 20:33:33 -0800863/** Opens a PCM by it's name.
864 * @param name The name of the PCM.
865 * The name is given in the format: <i>hw</i>:<b>card</b>,<b>device</b>
866 * @param flags Specify characteristics and functionality about the pcm.
867 * May be a bitwise AND of the following:
868 * - @ref PCM_IN
869 * - @ref PCM_OUT
870 * - @ref PCM_MMAP
871 * - @ref PCM_NOIRQ
872 * - @ref PCM_MONOTONIC
873 * @param config The hardware and software parameters to open the PCM with.
Taylor Holbertone123a652017-01-13 21:39:48 -0800874 * @returns A PCM structure.
875 * If an error occurs allocating memory for the PCM, NULL is returned.
876 * Otherwise, client code should check that the PCM opened properly by calling @ref pcm_is_ready.
877 * If @ref pcm_is_ready, check @ref pcm_get_error for more information.
Taylor Holbertonc6f908e2016-12-24 20:33:33 -0800878 * @ingroup libtinyalsa-pcm
879 */
880struct pcm *pcm_open_by_name(const char *name,
881 unsigned int flags,
882 const struct pcm_config *config)
883{
884 unsigned int card, device;
885 if ((name[0] != 'h')
886 || (name[1] != 'w')
887 || (name[2] != ':')) {
888 return NULL;
889 } else if (sscanf(&name[3], "%u,%u", &card, &device) != 2) {
890 return NULL;
891 }
892 return pcm_open(card, device, flags, config);
893}
894
Taylor Holberton6d58e012016-10-01 18:32:30 -0400895/** Opens a PCM.
896 * @param card The card that the pcm belongs to.
897 * The default card is zero.
898 * @param device The device that the pcm belongs to.
899 * The default device is zero.
900 * @param flags Specify characteristics and functionality about the pcm.
901 * May be a bitwise AND of the following:
902 * - @ref PCM_IN
903 * - @ref PCM_OUT
904 * - @ref PCM_MMAP
905 * - @ref PCM_NOIRQ
906 * - @ref PCM_MONOTONIC
907 * @param config The hardware and software parameters to open the PCM with.
Taylor Holbertone123a652017-01-13 21:39:48 -0800908 * @returns A PCM structure.
909 * If an error occurs allocating memory for the PCM, NULL is returned.
910 * Otherwise, client code should check that the PCM opened properly by calling @ref pcm_is_ready.
911 * If @ref pcm_is_ready, check @ref pcm_get_error for more information.
Taylor Holberton8e1b1022016-11-19 10:34:50 -0800912 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -0400913 */
Simon Wilson1bd580f2011-06-02 15:58:41 -0700914struct pcm *pcm_open(unsigned int card, unsigned int device,
Taylor Holberton94803b02016-12-01 16:07:14 -0800915 unsigned int flags, const struct pcm_config *config)
Simon Wilson79d39652011-05-25 13:44:23 -0700916{
Simon Wilson79d39652011-05-25 13:44:23 -0700917 struct pcm *pcm;
918 struct snd_pcm_info info;
919 struct snd_pcm_hw_params params;
920 struct snd_pcm_sw_params sparams;
Simon Wilson1bd580f2011-06-02 15:58:41 -0700921 char fn[256];
Eric Laurent40b018e2011-06-18 10:10:23 -0700922 int rc;
Simon Wilson79d39652011-05-25 13:44:23 -0700923
924 pcm = calloc(1, sizeof(struct pcm));
Taylor Holbertonf319eb02016-10-14 20:05:30 -0400925 if (!pcm)
926 return &bad_pcm;
Simon Wilson79d39652011-05-25 13:44:23 -0700927
Taylor Holbertonf319eb02016-10-14 20:05:30 -0400928 if (config == NULL) {
929 config = &pcm->config;
Taylor Holberton94803b02016-12-01 16:07:14 -0800930 pcm->config.channels = 2;
931 pcm->config.rate = 48000;
932 pcm->config.period_size = 1024;
933 pcm->config.period_count = 4;
934 pcm->config.format = PCM_FORMAT_S16_LE;
935 pcm->config.start_threshold = config->period_count * config->period_size;
936 pcm->config.stop_threshold = config->period_count * config->period_size;
937 pcm->config.silence_threshold = 0;
Taylor Holbertonf319eb02016-10-14 20:05:30 -0400938 } else {
939 pcm->config = *config;
940 }
Simon Wilson79d39652011-05-25 13:44:23 -0700941
Simon Wilson1bd580f2011-06-02 15:58:41 -0700942 snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
943 flags & PCM_IN ? 'c' : 'p');
Simon Wilson79d39652011-05-25 13:44:23 -0700944
945 pcm->flags = flags;
Simon Wilson1bd580f2011-06-02 15:58:41 -0700946 pcm->fd = open(fn, O_RDWR);
Simon Wilson79d39652011-05-25 13:44:23 -0700947 if (pcm->fd < 0) {
Simon Wilson1bd580f2011-06-02 15:58:41 -0700948 oops(pcm, errno, "cannot open device '%s'", fn);
Simon Wilson79d39652011-05-25 13:44:23 -0700949 return pcm;
950 }
951
952 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
Simon Wilson851aa5c2011-05-30 21:18:26 -0700953 oops(pcm, errno, "cannot get info");
Liam Girdwood6be28f12011-10-13 12:59:51 -0700954 goto fail_close;
Simon Wilson79d39652011-05-25 13:44:23 -0700955 }
David Wagner4cddf192014-04-02 15:12:54 +0200956 pcm->subdevice = info.subdevice;
Simon Wilson79d39652011-05-25 13:44:23 -0700957
958 param_init(&params);
Simon Wilson79d39652011-05-25 13:44:23 -0700959 param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
960 pcm_format_to_alsa(config->format));
961 param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
962 SNDRV_PCM_SUBFORMAT_STD);
963 param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
964 param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
965 pcm_format_to_bits(config->format));
966 param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
967 pcm_format_to_bits(config->format) * config->channels);
968 param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
969 config->channels);
970 param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
971 param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
972
Liam Girdwood6be28f12011-10-13 12:59:51 -0700973 if (flags & PCM_NOIRQ) {
974
975 if (!(flags & PCM_MMAP)) {
976 oops(pcm, -EINVAL, "noirq only currently supported with mmap().");
977 goto fail;
978 }
979
980 params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
981 pcm->noirq_frames_per_msec = config->rate / 1000;
982 }
983
984 if (flags & PCM_MMAP)
985 param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
986 SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
987 else
988 param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
989 SNDRV_PCM_ACCESS_RW_INTERLEAVED);
990
Simon Wilson79d39652011-05-25 13:44:23 -0700991 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
992 oops(pcm, errno, "cannot set hw params");
Liam Girdwood6be28f12011-10-13 12:59:51 -0700993 goto fail_close;
Simon Wilson79d39652011-05-25 13:44:23 -0700994 }
995
Liam Girdwood6be28f12011-10-13 12:59:51 -0700996 /* get our refined hw_params */
Taylor Holberton94803b02016-12-01 16:07:14 -0800997 pcm->config.period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
998 pcm->config.period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
Liam Girdwood6be28f12011-10-13 12:59:51 -0700999 pcm->buffer_size = config->period_count * config->period_size;
1000
1001 if (flags & PCM_MMAP) {
1002 pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
1003 PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
1004 if (pcm->mmap_buffer == MAP_FAILED) {
1005 oops(pcm, -errno, "failed to mmap buffer %d bytes\n",
1006 pcm_frames_to_bytes(pcm, pcm->buffer_size));
1007 goto fail_close;
1008 }
1009 }
1010
1011
Simon Wilson79d39652011-05-25 13:44:23 -07001012 memset(&sparams, 0, sizeof(sparams));
Eric Laurent40b018e2011-06-18 10:10:23 -07001013 sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
Simon Wilson79d39652011-05-25 13:44:23 -07001014 sparams.period_step = 1;
1015 sparams.avail_min = 1;
John Grossman3bb114a2011-07-21 10:59:55 -07001016
Eric Laurent93e7b672012-08-22 16:18:14 -07001017 if (!config->start_threshold) {
1018 if (pcm->flags & PCM_IN)
1019 pcm->config.start_threshold = sparams.start_threshold = 1;
1020 else
1021 pcm->config.start_threshold = sparams.start_threshold =
1022 config->period_count * config->period_size / 2;
1023 } else
John Grossman3bb114a2011-07-21 10:59:55 -07001024 sparams.start_threshold = config->start_threshold;
1025
Liam Girdwood6be28f12011-10-13 12:59:51 -07001026 /* pick a high stop threshold - todo: does this need further tuning */
Eric Laurent35021132012-01-30 11:31:56 -08001027 if (!config->stop_threshold) {
1028 if (pcm->flags & PCM_IN)
1029 pcm->config.stop_threshold = sparams.stop_threshold =
1030 config->period_count * config->period_size * 10;
1031 else
1032 pcm->config.stop_threshold = sparams.stop_threshold =
1033 config->period_count * config->period_size;
1034 }
John Grossman3bb114a2011-07-21 10:59:55 -07001035 else
1036 sparams.stop_threshold = config->stop_threshold;
1037
Simon Wilson79d39652011-05-25 13:44:23 -07001038 sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
1039 sparams.silence_size = 0;
John Grossman3bb114a2011-07-21 10:59:55 -07001040 sparams.silence_threshold = config->silence_threshold;
Liam Girdwood6be28f12011-10-13 12:59:51 -07001041 pcm->boundary = sparams.boundary = pcm->buffer_size;
John Grossman3bb114a2011-07-21 10:59:55 -07001042
Gabriel M. Beddingfield80085d42012-02-08 16:53:32 -06001043 while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
Liam Girdwood6be28f12011-10-13 12:59:51 -07001044 pcm->boundary *= 2;
Simon Wilson79d39652011-05-25 13:44:23 -07001045
1046 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
1047 oops(pcm, errno, "cannot set sw params");
1048 goto fail;
1049 }
1050
Eric Laurent40b018e2011-06-18 10:10:23 -07001051 rc = pcm_hw_mmap_status(pcm);
1052 if (rc < 0) {
1053 oops(pcm, rc, "mmap status failed");
1054 goto fail;
1055 }
1056
Glenn Kasten81012402013-08-22 15:11:48 -07001057#ifdef SNDRV_PCM_IOCTL_TTSTAMP
1058 if (pcm->flags & PCM_MONOTONIC) {
1059 int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
1060 rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
1061 if (rc < 0) {
1062 oops(pcm, rc, "cannot set timestamp type");
1063 goto fail;
1064 }
1065 }
1066#endif
1067
Simon Wilson79d39652011-05-25 13:44:23 -07001068 pcm->underruns = 0;
1069 return pcm;
1070
1071fail:
Liam Girdwood6be28f12011-10-13 12:59:51 -07001072 if (flags & PCM_MMAP)
1073 munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
1074fail_close:
Simon Wilson79d39652011-05-25 13:44:23 -07001075 close(pcm->fd);
1076 pcm->fd = -1;
1077 return pcm;
1078}
1079
Taylor Holberton6d58e012016-10-01 18:32:30 -04001080/** Checks if a PCM file has been opened without error.
1081 * @param pcm A PCM handle.
Taylor Holbertone123a652017-01-13 21:39:48 -08001082 * May be NULL.
1083 * @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 -04001084 * Otherwise, the function returns one.
Taylor Holberton8e1b1022016-11-19 10:34:50 -08001085 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -04001086 */
Taylor Holberton15d58482016-12-01 17:46:29 -08001087int pcm_is_ready(const struct pcm *pcm)
Simon Wilson79d39652011-05-25 13:44:23 -07001088{
Taylor Holbertone123a652017-01-13 21:39:48 -08001089 if (pcm != NULL) {
1090 return pcm->fd >= 0;
1091 }
1092 return 0;
Simon Wilson79d39652011-05-25 13:44:23 -07001093}
Simon Wilsond6458e62011-06-21 14:58:11 -07001094
Taylor Holberton558e5942016-12-04 13:42:28 -08001095/** Links two PCMs.
1096 * After this function is called, the two PCMs will prepare, start and stop in sync (at the same time).
1097 * If an error occurs, the error message will be written to @p pcm1.
1098 * @param pcm1 A PCM handle.
1099 * @param pcm2 Another PCM handle.
1100 * @return On success, zero; on failure, a negative number.
1101 * @ingroup libtinyalsa-pcm
1102 */
1103int pcm_link(struct pcm *pcm1, struct pcm *pcm2)
1104{
1105 int err = ioctl(pcm1->fd, SNDRV_PCM_IOCTL_LINK, pcm2->fd);
1106 if (err == -1) {
1107 return oops(pcm1, errno, "cannot link PCM");
1108 }
1109 return 0;
1110}
1111
1112/** Unlinks a PCM.
1113 * @see @ref pcm_link
1114 * @param pcm A PCM handle.
1115 * @return On success, zero; on failure, a negative number.
1116 * @ingroup libtinyalsa-pcm
1117 */
1118int pcm_unlink(struct pcm *pcm)
1119{
1120 int err = ioctl(pcm->fd, SNDRV_PCM_IOCTL_UNLINK);
1121 if (err == -1) {
1122 return oops(pcm, errno, "cannot unlink PCM");
1123 }
1124 return 0;
1125}
1126
Taylor Holberton6d58e012016-10-01 18:32:30 -04001127/** Prepares a PCM, if it has not been prepared already.
1128 * @param pcm A PCM handle.
1129 * @return On success, zero; on failure, a negative number.
Taylor Holberton8e1b1022016-11-19 10:34:50 -08001130 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -04001131 */
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +05301132int pcm_prepare(struct pcm *pcm)
Simon Wilsond6458e62011-06-21 14:58:11 -07001133{
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +05301134 if (pcm->prepared)
1135 return 0;
1136
Simon Wilsond6458e62011-06-21 14:58:11 -07001137 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
1138 return oops(pcm, errno, "cannot prepare channel");
Liam Girdwood6be28f12011-10-13 12:59:51 -07001139
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +05301140 pcm->prepared = 1;
1141 return 0;
1142}
1143
Taylor Holberton6d58e012016-10-01 18:32:30 -04001144/** Starts a PCM.
1145 * If the PCM has not been prepared,
1146 * it is prepared in this function.
1147 * @param pcm A PCM handle.
1148 * @return On success, zero; on failure, a negative number.
Taylor Holberton8e1b1022016-11-19 10:34:50 -08001149 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -04001150 */
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +05301151int pcm_start(struct pcm *pcm)
1152{
1153 int prepare_error = pcm_prepare(pcm);
1154 if (prepare_error)
1155 return prepare_error;
1156
Liam Girdwood6be28f12011-10-13 12:59:51 -07001157 if (pcm->flags & PCM_MMAP)
1158 pcm_sync_ptr(pcm, 0);
1159
Simon Wilsond6458e62011-06-21 14:58:11 -07001160 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
1161 return oops(pcm, errno, "cannot start channel");
1162
Liam Girdwood6be28f12011-10-13 12:59:51 -07001163 pcm->running = 1;
Simon Wilsond6458e62011-06-21 14:58:11 -07001164 return 0;
1165}
1166
Taylor Holberton6d58e012016-10-01 18:32:30 -04001167/** Stops a PCM.
1168 * @param pcm A PCM handle.
1169 * @return On success, zero; on failure, a negative number.
Taylor Holberton8e1b1022016-11-19 10:34:50 -08001170 * @ingroup libtinyalsa-pcm
Taylor Holberton6d58e012016-10-01 18:32:30 -04001171 */
Simon Wilsond6458e62011-06-21 14:58:11 -07001172int pcm_stop(struct pcm *pcm)
1173{
1174 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
1175 return oops(pcm, errno, "cannot stop channel");
1176
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +05301177 pcm->prepared = 0;
Liam Girdwood6be28f12011-10-13 12:59:51 -07001178 pcm->running = 0;
Simon Wilsond6458e62011-06-21 14:58:11 -07001179 return 0;
1180}
1181
Liam Girdwood6be28f12011-10-13 12:59:51 -07001182static inline int pcm_mmap_playback_avail(struct pcm *pcm)
1183{
1184 int avail;
1185
1186 avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
1187
1188 if (avail < 0)
1189 avail += pcm->boundary;
StevenNANb0fc3e92014-03-17 11:14:49 +08001190 else if (avail >= (int)pcm->boundary)
Liam Girdwood6be28f12011-10-13 12:59:51 -07001191 avail -= pcm->boundary;
1192
1193 return avail;
1194}
1195
1196static inline int pcm_mmap_capture_avail(struct pcm *pcm)
1197{
1198 int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr;
1199 if (avail < 0)
1200 avail += pcm->boundary;
1201 return avail;
1202}
1203
1204static inline int pcm_mmap_avail(struct pcm *pcm)
1205{
1206 pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
1207 if (pcm->flags & PCM_IN)
1208 return pcm_mmap_capture_avail(pcm);
1209 else
1210 return pcm_mmap_playback_avail(pcm);
1211}
1212
1213static void pcm_mmap_appl_forward(struct pcm *pcm, int frames)
1214{
1215 unsigned int appl_ptr = pcm->mmap_control->appl_ptr;
1216 appl_ptr += frames;
1217
1218 /* check for boundary wrap */
1219 if (appl_ptr > pcm->boundary)
1220 appl_ptr -= pcm->boundary;
1221 pcm->mmap_control->appl_ptr = appl_ptr;
1222}
1223
1224int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
1225 unsigned int *frames)
1226{
1227 unsigned int continuous, copy_frames, avail;
1228
1229 /* return the mmap buffer */
1230 *areas = pcm->mmap_buffer;
1231
1232 /* and the application offset in frames */
1233 *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size;
1234
1235 avail = pcm_mmap_avail(pcm);
1236 if (avail > pcm->buffer_size)
1237 avail = pcm->buffer_size;
1238 continuous = pcm->buffer_size - *offset;
1239
1240 /* we can only copy frames if the are availabale and continuos */
1241 copy_frames = *frames;
1242 if (copy_frames > avail)
1243 copy_frames = avail;
1244 if (copy_frames > continuous)
1245 copy_frames = continuous;
1246 *frames = copy_frames;
1247
1248 return 0;
1249}
1250
1251int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames)
1252{
Taylor Holberton72e44222016-11-22 09:54:47 -08001253 int ret;
1254
Taylor Holberton73466c02016-10-01 12:51:59 -04001255 /* not used */
1256 (void) offset;
1257
Liam Girdwood6be28f12011-10-13 12:59:51 -07001258 /* update the application pointer in userspace and kernel */
1259 pcm_mmap_appl_forward(pcm, frames);
Taylor Holberton72e44222016-11-22 09:54:47 -08001260 ret = pcm_sync_ptr(pcm, 0);
Taylor Holbertone123a652017-01-13 21:39:48 -08001261 if (ret != 0){
1262 printf("%d\n", ret);
Taylor Holberton72e44222016-11-22 09:54:47 -08001263 return ret;
Taylor Holbertone123a652017-01-13 21:39:48 -08001264 }
Liam Girdwood6be28f12011-10-13 12:59:51 -07001265
1266 return frames;
1267}
1268
1269int pcm_avail_update(struct pcm *pcm)
1270{
1271 pcm_sync_ptr(pcm, 0);
1272 return pcm_mmap_avail(pcm);
1273}
1274
1275int pcm_state(struct pcm *pcm)
1276{
1277 int err = pcm_sync_ptr(pcm, 0);
1278 if (err < 0)
1279 return err;
1280
1281 return pcm->mmap_status->state;
1282}
1283
Taylor Holberton17a10242016-11-23 13:18:24 -08001284/** Waits for frames to be available for read or write operations.
1285 * @param pcm A PCM handle.
1286 * @param timeout The maximum amount of time to wait for, in terms of milliseconds.
1287 * @returns If frames became available, one is returned.
1288 * If a timeout occured, zero is returned.
1289 * If an error occured, a negative number is returned.
1290 * @ingroup libtinyalsa-pcm
1291 */
Liam Girdwood6be28f12011-10-13 12:59:51 -07001292int pcm_wait(struct pcm *pcm, int timeout)
1293{
1294 struct pollfd pfd;
Liam Girdwood6be28f12011-10-13 12:59:51 -07001295 int err;
1296
1297 pfd.fd = pcm->fd;
Apelete Seketeli84889d02014-02-14 14:34:32 +01001298 pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
Liam Girdwood6be28f12011-10-13 12:59:51 -07001299
1300 do {
1301 /* let's wait for avail or timeout */
1302 err = poll(&pfd, 1, timeout);
1303 if (err < 0)
1304 return -errno;
1305
1306 /* timeout ? */
1307 if (err == 0)
1308 return 0;
1309
1310 /* have we been interrupted ? */
1311 if (errno == -EINTR)
1312 continue;
1313
1314 /* check for any errors */
1315 if (pfd.revents & (POLLERR | POLLNVAL)) {
1316 switch (pcm_state(pcm)) {
1317 case PCM_STATE_XRUN:
1318 return -EPIPE;
1319 case PCM_STATE_SUSPENDED:
1320 return -ESTRPIPE;
1321 case PCM_STATE_DISCONNECTED:
1322 return -ENODEV;
1323 default:
1324 return -EIO;
1325 }
1326 }
1327 /* poll again if fd not ready for IO */
1328 } while (!(pfd.revents & (POLLIN | POLLOUT)));
1329
1330 return 1;
1331}
1332
Eric Laurentbb7c5df2013-09-16 14:31:17 -07001333int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes)
Liam Girdwood6be28f12011-10-13 12:59:51 -07001334{
1335 int err = 0, frames, avail;
1336 unsigned int offset = 0, count;
1337
1338 if (bytes == 0)
1339 return 0;
1340
1341 count = pcm_bytes_to_frames(pcm, bytes);
1342
1343 while (count > 0) {
1344
1345 /* get the available space for writing new frames */
1346 avail = pcm_avail_update(pcm);
1347 if (avail < 0) {
1348 fprintf(stderr, "cannot determine available mmap frames");
1349 return err;
1350 }
1351
1352 /* start the audio if we reach the threshold */
1353 if (!pcm->running &&
1354 (pcm->buffer_size - avail) >= pcm->config.start_threshold) {
1355 if (pcm_start(pcm) < 0) {
1356 fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n",
1357 (unsigned int)pcm->mmap_status->hw_ptr,
1358 (unsigned int)pcm->mmap_control->appl_ptr,
1359 avail);
1360 return -errno;
1361 }
1362 }
1363
1364 /* sleep until we have space to write new frames */
1365 if (pcm->running &&
1366 (unsigned int)avail < pcm->mmap_control->avail_min) {
1367 int time = -1;
1368
1369 if (pcm->flags & PCM_NOIRQ)
1370 time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min)
1371 / pcm->noirq_frames_per_msec;
1372
1373 err = pcm_wait(pcm, time);
1374 if (err < 0) {
Omair Mohammed Abdullahc9032a02013-01-31 16:35:39 +05301375 pcm->prepared = 0;
Liam Girdwood6be28f12011-10-13 12:59:51 -07001376 pcm->running = 0;
1377 fprintf(stderr, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
1378 (unsigned int)pcm->mmap_status->hw_ptr,
1379 (unsigned int)pcm->mmap_control->appl_ptr,
1380 avail);
1381 pcm->mmap_control->appl_ptr = 0;
1382 return err;
1383 }
1384 continue;
1385 }
1386
1387 frames = count;
1388 if (frames > avail)
1389 frames = avail;
1390
1391 if (!frames)
1392 break;
1393
1394 /* copy frames from buffer */
Eric Laurentbb7c5df2013-09-16 14:31:17 -07001395 frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames);
Liam Girdwood6be28f12011-10-13 12:59:51 -07001396 if (frames < 0) {
1397 fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
1398 (unsigned int)pcm->mmap_status->hw_ptr,
1399 (unsigned int)pcm->mmap_control->appl_ptr,
1400 avail);
1401 return frames;
1402 }
1403
1404 offset += frames;
1405 count -= frames;
1406 }
1407
Liam Girdwood6be28f12011-10-13 12:59:51 -07001408 return 0;
1409}
Eric Laurentbb7c5df2013-09-16 14:31:17 -07001410
1411int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count)
1412{
1413 if ((~pcm->flags) & (PCM_OUT | PCM_MMAP))
1414 return -ENOSYS;
1415
1416 return pcm_mmap_transfer(pcm, (void *)data, count);
1417}
1418
1419int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count)
1420{
1421 if ((~pcm->flags) & (PCM_IN | PCM_MMAP))
1422 return -ENOSYS;
1423
1424 return pcm_mmap_transfer(pcm, data, count);
1425}
Hardik T Shah9ecb93f2014-04-10 18:03:52 +05301426
Taylor Holberton17a10242016-11-23 13:18:24 -08001427/** Gets the delay of the PCM, in terms of frames.
1428 * @param pcm A PCM handle.
1429 * @returns On success, the delay of the PCM.
1430 * On failure, a negative number.
1431 * @ingroup libtinyalsa-pcm
1432 */
Hardik T Shah9ecb93f2014-04-10 18:03:52 +05301433long pcm_get_delay(struct pcm *pcm)
1434{
1435 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DELAY, &pcm->pcm_delay) < 0)
1436 return -1;
1437
1438 return pcm->pcm_delay;
1439}
Taylor Holberton6d58e012016-10-01 18:32:30 -04001440