• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* 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>
36 #include <poll.h>
37 
38 #include <sys/ioctl.h>
39 #include <sys/mman.h>
40 #include <sys/time.h>
41 #include <limits.h>
42 
43 #include <linux/ioctl.h>
44 #define __force
45 #define __bitwise
46 #define __user
47 #include <sound/asound.h>
48 
49 #include <tinyalsa/asoundlib.h>
50 
51 #define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
52 #define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
53 
param_is_mask(int p)54 static inline int param_is_mask(int p)
55 {
56     return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
57         (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
58 }
59 
param_is_interval(int p)60 static inline int param_is_interval(int p)
61 {
62     return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
63         (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
64 }
65 
param_to_interval(struct snd_pcm_hw_params * p,int n)66 static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
67 {
68     return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
69 }
70 
param_to_mask(struct snd_pcm_hw_params * p,int n)71 static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
72 {
73     return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
74 }
75 
param_set_mask(struct snd_pcm_hw_params * p,int n,unsigned int bit)76 static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
77 {
78     if (bit >= SNDRV_MASK_MAX)
79         return;
80     if (param_is_mask(n)) {
81         struct snd_mask *m = param_to_mask(p, n);
82         m->bits[0] = 0;
83         m->bits[1] = 0;
84         m->bits[bit >> 5] |= (1 << (bit & 31));
85     }
86 }
87 
param_set_min(struct snd_pcm_hw_params * p,int n,unsigned int val)88 static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
89 {
90     if (param_is_interval(n)) {
91         struct snd_interval *i = param_to_interval(p, n);
92         i->min = val;
93     }
94 }
95 
param_get_min(struct snd_pcm_hw_params * p,int n)96 static unsigned int param_get_min(struct snd_pcm_hw_params *p, int n)
97 {
98     if (param_is_interval(n)) {
99         struct snd_interval *i = param_to_interval(p, n);
100         return i->min;
101     }
102     return 0;
103 }
104 
param_get_max(struct snd_pcm_hw_params * p,int n)105 static unsigned int param_get_max(struct snd_pcm_hw_params *p, int n)
106 {
107     if (param_is_interval(n)) {
108         struct snd_interval *i = param_to_interval(p, n);
109         return i->max;
110     }
111     return 0;
112 }
113 
param_set_int(struct snd_pcm_hw_params * p,int n,unsigned int val)114 static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
115 {
116     if (param_is_interval(n)) {
117         struct snd_interval *i = param_to_interval(p, n);
118         i->min = val;
119         i->max = val;
120         i->integer = 1;
121     }
122 }
123 
param_get_int(struct snd_pcm_hw_params * p,int n)124 static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n)
125 {
126     if (param_is_interval(n)) {
127         struct snd_interval *i = param_to_interval(p, n);
128         if (i->integer)
129             return i->max;
130     }
131     return 0;
132 }
133 
param_init(struct snd_pcm_hw_params * p)134 static void param_init(struct snd_pcm_hw_params *p)
135 {
136     int n;
137 
138     memset(p, 0, sizeof(*p));
139     for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
140          n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
141             struct snd_mask *m = param_to_mask(p, n);
142             m->bits[0] = ~0;
143             m->bits[1] = ~0;
144     }
145     for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
146          n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
147             struct snd_interval *i = param_to_interval(p, n);
148             i->min = 0;
149             i->max = ~0;
150     }
151     p->rmask = ~0U;
152     p->cmask = 0;
153     p->info = ~0U;
154 }
155 
156 #define PCM_ERROR_MAX 128
157 
158 struct pcm {
159     int fd;
160     unsigned int flags;
161     int running:1;
162     int underruns;
163     unsigned int buffer_size;
164     unsigned int boundary;
165     char error[PCM_ERROR_MAX];
166     struct pcm_config config;
167     struct snd_pcm_mmap_status *mmap_status;
168     struct snd_pcm_mmap_control *mmap_control;
169     struct snd_pcm_sync_ptr *sync_ptr;
170     void *mmap_buffer;
171     unsigned int noirq_frames_per_msec;
172     int wait_for_avail_min;
173 };
174 
pcm_get_buffer_size(struct pcm * pcm)175 unsigned int pcm_get_buffer_size(struct pcm *pcm)
176 {
177     return pcm->buffer_size;
178 }
179 
pcm_get_error(struct pcm * pcm)180 const char* pcm_get_error(struct pcm *pcm)
181 {
182     return pcm->error;
183 }
184 
oops(struct pcm * pcm,int e,const char * fmt,...)185 static int oops(struct pcm *pcm, int e, const char *fmt, ...)
186 {
187     va_list ap;
188     int sz;
189 
190     va_start(ap, fmt);
191     vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
192     va_end(ap);
193     sz = strlen(pcm->error);
194 
195     if (errno)
196         snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
197                  ": %s", strerror(e));
198     return -1;
199 }
200 
pcm_format_to_alsa(enum pcm_format format)201 static unsigned int pcm_format_to_alsa(enum pcm_format format)
202 {
203     switch (format) {
204     case PCM_FORMAT_S32_LE:
205         return SNDRV_PCM_FORMAT_S32_LE;
206     case PCM_FORMAT_S8:
207         return SNDRV_PCM_FORMAT_S8;
208     case PCM_FORMAT_S24_LE:
209         return SNDRV_PCM_FORMAT_S24_LE;
210     default:
211     case PCM_FORMAT_S16_LE:
212         return SNDRV_PCM_FORMAT_S16_LE;
213     };
214 }
215 
pcm_format_to_bits(enum pcm_format format)216 static unsigned int pcm_format_to_bits(enum pcm_format format)
217 {
218     switch (format) {
219     case PCM_FORMAT_S32_LE:
220         return 32;
221     default:
222     case PCM_FORMAT_S16_LE:
223         return 16;
224     };
225 }
226 
pcm_bytes_to_frames(struct pcm * pcm,unsigned int bytes)227 unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes)
228 {
229     return bytes / (pcm->config.channels *
230         (pcm_format_to_bits(pcm->config.format) >> 3));
231 }
232 
pcm_frames_to_bytes(struct pcm * pcm,unsigned int frames)233 unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames)
234 {
235     return frames * pcm->config.channels *
236         (pcm_format_to_bits(pcm->config.format) >> 3);
237 }
238 
pcm_sync_ptr(struct pcm * pcm,int flags)239 static int pcm_sync_ptr(struct pcm *pcm, int flags) {
240     if (pcm->sync_ptr) {
241         pcm->sync_ptr->flags = flags;
242         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
243             return -1;
244     }
245     return 0;
246 }
247 
pcm_hw_mmap_status(struct pcm * pcm)248 static int pcm_hw_mmap_status(struct pcm *pcm) {
249 
250     if (pcm->sync_ptr)
251         return 0;
252 
253     int page_size = sysconf(_SC_PAGE_SIZE);
254     pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
255                             pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
256     if (pcm->mmap_status == MAP_FAILED)
257         pcm->mmap_status = NULL;
258     if (!pcm->mmap_status)
259         goto mmap_error;
260 
261     pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
262                              MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
263     if (pcm->mmap_control == MAP_FAILED)
264         pcm->mmap_control = NULL;
265     if (!pcm->mmap_control) {
266         munmap(pcm->mmap_status, page_size);
267         pcm->mmap_status = NULL;
268         goto mmap_error;
269     }
270     if (pcm->flags & PCM_MMAP)
271         pcm->mmap_control->avail_min = pcm->config.avail_min;
272     else
273         pcm->mmap_control->avail_min = 1;
274 
275     return 0;
276 
277 mmap_error:
278 
279     pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr));
280     if (!pcm->sync_ptr)
281         return -ENOMEM;
282     pcm->mmap_status = &pcm->sync_ptr->s.status;
283     pcm->mmap_control = &pcm->sync_ptr->c.control;
284     if (pcm->flags & PCM_MMAP)
285         pcm->mmap_control->avail_min = pcm->config.avail_min;
286     else
287         pcm->mmap_control->avail_min = 1;
288 
289     pcm_sync_ptr(pcm, 0);
290 
291     return 0;
292 }
293 
pcm_hw_munmap_status(struct pcm * pcm)294 static void pcm_hw_munmap_status(struct pcm *pcm) {
295     if (pcm->sync_ptr) {
296         free(pcm->sync_ptr);
297         pcm->sync_ptr = NULL;
298     } else {
299         int page_size = sysconf(_SC_PAGE_SIZE);
300         if (pcm->mmap_status)
301             munmap(pcm->mmap_status, page_size);
302         if (pcm->mmap_control)
303             munmap(pcm->mmap_control, page_size);
304     }
305     pcm->mmap_status = NULL;
306     pcm->mmap_control = NULL;
307 }
308 
pcm_areas_copy(struct pcm * pcm,unsigned int pcm_offset,const char * src,unsigned int src_offset,unsigned int frames)309 static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
310                           const char *src, unsigned int src_offset,
311                           unsigned int frames)
312 {
313     int size_bytes = pcm_frames_to_bytes(pcm, frames);
314     int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset);
315     int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
316 
317     /* interleaved only atm */
318     memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
319            src + src_offset_bytes, size_bytes);
320     return 0;
321 }
322 
pcm_mmap_write_areas(struct pcm * pcm,const char * src,unsigned int offset,unsigned int size)323 static int pcm_mmap_write_areas(struct pcm *pcm, const char *src,
324                                 unsigned int offset, unsigned int size)
325 {
326     void *pcm_areas;
327     int commit;
328     unsigned int pcm_offset, frames, count = 0;
329 
330     while (size > 0) {
331         frames = size;
332         pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
333         pcm_areas_copy(pcm, pcm_offset, src, offset, frames);
334         commit = pcm_mmap_commit(pcm, pcm_offset, frames);
335         if (commit < 0) {
336             oops(pcm, commit, "failed to commit %d frames\n", frames);
337             return commit;
338         }
339 
340         offset += commit;
341         count += commit;
342         size -= commit;
343     }
344     return count;
345 }
346 
pcm_get_htimestamp(struct pcm * pcm,unsigned int * avail,struct timespec * tstamp)347 int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
348                        struct timespec *tstamp)
349 {
350     int frames;
351     int rc;
352     snd_pcm_uframes_t hw_ptr;
353 
354     if (!pcm_is_ready(pcm))
355         return -1;
356 
357     rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
358     if (rc < 0)
359         return -1;
360 
361     if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
362             (pcm->mmap_status->state != PCM_STATE_DRAINING))
363         return -1;
364 
365     *tstamp = pcm->mmap_status->tstamp;
366     if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
367         return -1;
368 
369     hw_ptr = pcm->mmap_status->hw_ptr;
370     if (pcm->flags & PCM_IN)
371         frames = hw_ptr - pcm->mmap_control->appl_ptr;
372     else
373         frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
374 
375     if (frames < 0)
376         frames += pcm->boundary;
377     else if (frames > (int)pcm->boundary)
378         frames -= pcm->boundary;
379 
380     *avail = (unsigned int)frames;
381 
382     return 0;
383 }
384 
pcm_write(struct pcm * pcm,const void * data,unsigned int count)385 int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
386 {
387     struct snd_xferi x;
388 
389     if (pcm->flags & PCM_IN)
390         return -EINVAL;
391 
392     x.buf = (void*)data;
393     x.frames = count / (pcm->config.channels *
394                         pcm_format_to_bits(pcm->config.format) / 8);
395 
396     for (;;) {
397         if (!pcm->running) {
398             if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))
399                 return oops(pcm, errno, "cannot prepare channel");
400             if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
401                 return oops(pcm, errno, "cannot write initial data");
402             pcm->running = 1;
403             return 0;
404         }
405         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
406             pcm->running = 0;
407             if (errno == EPIPE) {
408                 /* we failed to make our window -- try to restart if we are
409                  * allowed to do so.  Otherwise, simply allow the EPIPE error to
410                  * propagate up to the app level */
411                 pcm->underruns++;
412                 if (pcm->flags & PCM_NORESTART)
413                     return -EPIPE;
414                 continue;
415             }
416             return oops(pcm, errno, "cannot write stream data");
417         }
418         return 0;
419     }
420 }
421 
pcm_read(struct pcm * pcm,void * data,unsigned int count)422 int pcm_read(struct pcm *pcm, void *data, unsigned int count)
423 {
424     struct snd_xferi x;
425 
426     if (!(pcm->flags & PCM_IN))
427         return -EINVAL;
428 
429     x.buf = data;
430     x.frames = count / (pcm->config.channels *
431                         pcm_format_to_bits(pcm->config.format) / 8);
432 
433     for (;;) {
434         if (!pcm->running) {
435             if (pcm_start(pcm) < 0) {
436                 fprintf(stderr, "start error");
437                 return -errno;
438             }
439         }
440         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
441             pcm->running = 0;
442             if (errno == EPIPE) {
443                     /* we failed to make our window -- try to restart */
444                 pcm->underruns++;
445                 continue;
446             }
447             return oops(pcm, errno, "cannot read stream data");
448         }
449         return 0;
450     }
451 }
452 
453 static struct pcm bad_pcm = {
454     .fd = -1,
455 };
456 
pcm_params_get(unsigned int card,unsigned int device,unsigned int flags)457 struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
458                                   unsigned int flags)
459 {
460     struct snd_pcm_hw_params *params;
461     char fn[256];
462     int fd;
463 
464     snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
465              flags & PCM_IN ? 'c' : 'p');
466 
467     fd = open(fn, O_RDWR);
468     if (fd < 0) {
469         fprintf(stderr, "cannot open device '%s'\n", fn);
470         goto err_open;
471     }
472 
473     params = calloc(1, sizeof(struct snd_pcm_hw_params));
474     if (!params)
475         goto err_calloc;
476 
477     param_init(params);
478     if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
479         fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
480         goto err_hw_refine;
481     }
482 
483     close(fd);
484 
485     return (struct pcm_params *)params;
486 
487 err_hw_refine:
488     free(params);
489 err_calloc:
490     close(fd);
491 err_open:
492     return NULL;
493 }
494 
pcm_params_free(struct pcm_params * pcm_params)495 void pcm_params_free(struct pcm_params *pcm_params)
496 {
497     struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
498 
499     if (params)
500         free(params);
501 }
502 
pcm_param_to_alsa(enum pcm_param param)503 static int pcm_param_to_alsa(enum pcm_param param)
504 {
505     switch (param) {
506     case PCM_PARAM_SAMPLE_BITS:
507         return SNDRV_PCM_HW_PARAM_SAMPLE_BITS;
508         break;
509     case PCM_PARAM_FRAME_BITS:
510         return SNDRV_PCM_HW_PARAM_FRAME_BITS;
511         break;
512     case PCM_PARAM_CHANNELS:
513         return SNDRV_PCM_HW_PARAM_CHANNELS;
514         break;
515     case PCM_PARAM_RATE:
516         return SNDRV_PCM_HW_PARAM_RATE;
517         break;
518     case PCM_PARAM_PERIOD_TIME:
519         return SNDRV_PCM_HW_PARAM_PERIOD_TIME;
520         break;
521     case PCM_PARAM_PERIOD_SIZE:
522         return SNDRV_PCM_HW_PARAM_PERIOD_SIZE;
523         break;
524     case PCM_PARAM_PERIOD_BYTES:
525         return SNDRV_PCM_HW_PARAM_PERIOD_BYTES;
526         break;
527     case PCM_PARAM_PERIODS:
528         return SNDRV_PCM_HW_PARAM_PERIODS;
529         break;
530     case PCM_PARAM_BUFFER_TIME:
531         return SNDRV_PCM_HW_PARAM_BUFFER_TIME;
532         break;
533     case PCM_PARAM_BUFFER_SIZE:
534         return SNDRV_PCM_HW_PARAM_BUFFER_SIZE;
535         break;
536     case PCM_PARAM_BUFFER_BYTES:
537         return SNDRV_PCM_HW_PARAM_BUFFER_BYTES;
538         break;
539     case PCM_PARAM_TICK_TIME:
540         return SNDRV_PCM_HW_PARAM_TICK_TIME;
541         break;
542 
543     default:
544         return -1;
545     }
546 }
547 
pcm_params_get_min(struct pcm_params * pcm_params,enum pcm_param param)548 unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
549                                 enum pcm_param param)
550 {
551     struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
552     int p;
553 
554     if (!params)
555         return 0;
556 
557     p = pcm_param_to_alsa(param);
558     if (p < 0)
559         return 0;
560 
561     return param_get_min(params, p);
562 }
563 
pcm_params_get_max(struct pcm_params * pcm_params,enum pcm_param param)564 unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
565                                 enum pcm_param param)
566 {
567     struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
568     int p;
569 
570     if (!params)
571         return 0;
572 
573     p = pcm_param_to_alsa(param);
574     if (p < 0)
575         return 0;
576 
577     return param_get_max(params, p);
578 }
579 
pcm_close(struct pcm * pcm)580 int pcm_close(struct pcm *pcm)
581 {
582     if (pcm == &bad_pcm)
583         return 0;
584 
585     pcm_hw_munmap_status(pcm);
586 
587     if (pcm->flags & PCM_MMAP) {
588         pcm_stop(pcm);
589         munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
590     }
591 
592     if (pcm->fd >= 0)
593         close(pcm->fd);
594     pcm->running = 0;
595     pcm->buffer_size = 0;
596     pcm->fd = -1;
597     free(pcm);
598     return 0;
599 }
600 
pcm_open(unsigned int card,unsigned int device,unsigned int flags,struct pcm_config * config)601 struct pcm *pcm_open(unsigned int card, unsigned int device,
602                      unsigned int flags, struct pcm_config *config)
603 {
604     struct pcm *pcm;
605     struct snd_pcm_info info;
606     struct snd_pcm_hw_params params;
607     struct snd_pcm_sw_params sparams;
608     char fn[256];
609     int rc;
610 
611     pcm = calloc(1, sizeof(struct pcm));
612     if (!pcm || !config)
613         return &bad_pcm; /* TODO: could support default config here */
614 
615     pcm->config = *config;
616 
617     snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
618              flags & PCM_IN ? 'c' : 'p');
619 
620     pcm->flags = flags;
621     pcm->fd = open(fn, O_RDWR);
622     if (pcm->fd < 0) {
623         oops(pcm, errno, "cannot open device '%s'", fn);
624         return pcm;
625     }
626 
627     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
628         oops(pcm, errno, "cannot get info");
629         goto fail_close;
630     }
631 
632     param_init(&params);
633     param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
634                    pcm_format_to_alsa(config->format));
635     param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
636                    SNDRV_PCM_SUBFORMAT_STD);
637     param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
638     param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
639                   pcm_format_to_bits(config->format));
640     param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
641                   pcm_format_to_bits(config->format) * config->channels);
642     param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
643                   config->channels);
644     param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
645     param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
646 
647     if (flags & PCM_NOIRQ) {
648 
649         if (!(flags & PCM_MMAP)) {
650             oops(pcm, -EINVAL, "noirq only currently supported with mmap().");
651             goto fail;
652         }
653 
654         params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
655         pcm->noirq_frames_per_msec = config->rate / 1000;
656     }
657 
658     if (flags & PCM_MMAP)
659         param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
660                    SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
661     else
662         param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
663                    SNDRV_PCM_ACCESS_RW_INTERLEAVED);
664 
665     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
666         oops(pcm, errno, "cannot set hw params");
667         goto fail_close;
668     }
669 
670     /* get our refined hw_params */
671     config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
672     config->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
673     pcm->buffer_size = config->period_count * config->period_size;
674 
675     if (flags & PCM_MMAP) {
676         pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
677                                 PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
678         if (pcm->mmap_buffer == MAP_FAILED) {
679             oops(pcm, -errno, "failed to mmap buffer %d bytes\n",
680                  pcm_frames_to_bytes(pcm, pcm->buffer_size));
681             goto fail_close;
682         }
683     }
684 
685 
686     memset(&sparams, 0, sizeof(sparams));
687     sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
688     sparams.period_step = 1;
689 
690     if (!config->start_threshold) {
691         if (pcm->flags & PCM_IN)
692             pcm->config.start_threshold = sparams.start_threshold = 1;
693         else
694             pcm->config.start_threshold = sparams.start_threshold =
695                 config->period_count * config->period_size / 2;
696     } else
697         sparams.start_threshold = config->start_threshold;
698 
699     /* pick a high stop threshold - todo: does this need further tuning */
700     if (!config->stop_threshold) {
701         if (pcm->flags & PCM_IN)
702             pcm->config.stop_threshold = sparams.stop_threshold =
703                 config->period_count * config->period_size * 10;
704         else
705             pcm->config.stop_threshold = sparams.stop_threshold =
706                 config->period_count * config->period_size;
707     }
708     else
709         sparams.stop_threshold = config->stop_threshold;
710 
711     if (!pcm->config.avail_min) {
712         if (pcm->flags & PCM_MMAP)
713             pcm->config.avail_min = sparams.avail_min = pcm->config.period_size;
714         else
715             pcm->config.avail_min = sparams.avail_min = 1;
716     } else
717         sparams.avail_min = config->avail_min;
718 
719     sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
720     sparams.silence_size = 0;
721     sparams.silence_threshold = config->silence_threshold;
722     pcm->boundary = sparams.boundary = pcm->buffer_size;
723 
724     while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
725 		pcm->boundary *= 2;
726 
727     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
728         oops(pcm, errno, "cannot set sw params");
729         goto fail;
730     }
731 
732     rc = pcm_hw_mmap_status(pcm);
733     if (rc < 0) {
734         oops(pcm, rc, "mmap status failed");
735         goto fail;
736     }
737 
738     pcm->underruns = 0;
739     return pcm;
740 
741 fail:
742     if (flags & PCM_MMAP)
743         munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
744 fail_close:
745     close(pcm->fd);
746     pcm->fd = -1;
747     return pcm;
748 }
749 
pcm_is_ready(struct pcm * pcm)750 int pcm_is_ready(struct pcm *pcm)
751 {
752     return pcm->fd >= 0;
753 }
754 
pcm_start(struct pcm * pcm)755 int pcm_start(struct pcm *pcm)
756 {
757     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
758         return oops(pcm, errno, "cannot prepare channel");
759 
760     if (pcm->flags & PCM_MMAP)
761 	    pcm_sync_ptr(pcm, 0);
762 
763     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
764         return oops(pcm, errno, "cannot start channel");
765 
766     pcm->running = 1;
767     return 0;
768 }
769 
pcm_stop(struct pcm * pcm)770 int pcm_stop(struct pcm *pcm)
771 {
772     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
773         return oops(pcm, errno, "cannot stop channel");
774 
775     pcm->running = 0;
776     return 0;
777 }
778 
pcm_mmap_playback_avail(struct pcm * pcm)779 static inline int pcm_mmap_playback_avail(struct pcm *pcm)
780 {
781     int avail;
782 
783     avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
784 
785     if (avail < 0)
786         avail += pcm->boundary;
787     else if (avail > (int)pcm->boundary)
788         avail -= pcm->boundary;
789 
790     return avail;
791 }
792 
pcm_mmap_capture_avail(struct pcm * pcm)793 static inline int pcm_mmap_capture_avail(struct pcm *pcm)
794 {
795     int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr;
796     if (avail < 0)
797         avail += pcm->boundary;
798     return avail;
799 }
800 
pcm_mmap_avail(struct pcm * pcm)801 static inline int pcm_mmap_avail(struct pcm *pcm)
802 {
803     pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
804     if (pcm->flags & PCM_IN)
805         return pcm_mmap_capture_avail(pcm);
806     else
807         return pcm_mmap_playback_avail(pcm);
808 }
809 
pcm_mmap_appl_forward(struct pcm * pcm,int frames)810 static void pcm_mmap_appl_forward(struct pcm *pcm, int frames)
811 {
812     unsigned int appl_ptr = pcm->mmap_control->appl_ptr;
813     appl_ptr += frames;
814 
815     /* check for boundary wrap */
816     if (appl_ptr > pcm->boundary)
817          appl_ptr -= pcm->boundary;
818     pcm->mmap_control->appl_ptr = appl_ptr;
819 }
820 
pcm_mmap_begin(struct pcm * pcm,void ** areas,unsigned int * offset,unsigned int * frames)821 int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
822                    unsigned int *frames)
823 {
824     unsigned int continuous, copy_frames, avail;
825 
826     /* return the mmap buffer */
827     *areas = pcm->mmap_buffer;
828 
829     /* and the application offset in frames */
830     *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size;
831 
832     avail = pcm_mmap_avail(pcm);
833     if (avail > pcm->buffer_size)
834         avail = pcm->buffer_size;
835     continuous = pcm->buffer_size - *offset;
836 
837     /* we can only copy frames if the are availabale and continuos */
838     copy_frames = *frames;
839     if (copy_frames > avail)
840         copy_frames = avail;
841     if (copy_frames > continuous)
842         copy_frames = continuous;
843     *frames = copy_frames;
844 
845     return 0;
846 }
847 
pcm_mmap_commit(struct pcm * pcm,unsigned int offset,unsigned int frames)848 int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames)
849 {
850     /* update the application pointer in userspace and kernel */
851     pcm_mmap_appl_forward(pcm, frames);
852     pcm_sync_ptr(pcm, 0);
853 
854     return frames;
855 }
856 
pcm_avail_update(struct pcm * pcm)857 int pcm_avail_update(struct pcm *pcm)
858 {
859     pcm_sync_ptr(pcm, 0);
860     return pcm_mmap_avail(pcm);
861 }
862 
pcm_state(struct pcm * pcm)863 int pcm_state(struct pcm *pcm)
864 {
865     int err = pcm_sync_ptr(pcm, 0);
866     if (err < 0)
867         return err;
868 
869     return pcm->mmap_status->state;
870 }
871 
pcm_set_avail_min(struct pcm * pcm,int avail_min)872 int pcm_set_avail_min(struct pcm *pcm, int avail_min)
873 {
874     if ((~pcm->flags) & (PCM_MMAP | PCM_NOIRQ))
875         return -ENOSYS;
876 
877     pcm->config.avail_min = avail_min;
878     return 0;
879 }
880 
pcm_wait(struct pcm * pcm,int timeout)881 int pcm_wait(struct pcm *pcm, int timeout)
882 {
883     struct pollfd pfd;
884     int err;
885 
886     pfd.fd = pcm->fd;
887     pfd.events = POLLOUT | POLLERR | POLLNVAL;
888 
889     do {
890         /* let's wait for avail or timeout */
891         err = poll(&pfd, 1, timeout);
892         if (err < 0)
893             return -errno;
894 
895         /* timeout ? */
896         if (err == 0)
897             return 0;
898 
899         /* have we been interrupted ? */
900         if (errno == -EINTR)
901             continue;
902 
903         /* check for any errors */
904         if (pfd.revents & (POLLERR | POLLNVAL)) {
905             switch (pcm_state(pcm)) {
906             case PCM_STATE_XRUN:
907                 return -EPIPE;
908             case PCM_STATE_SUSPENDED:
909                 return -ESTRPIPE;
910             case PCM_STATE_DISCONNECTED:
911                 return -ENODEV;
912             default:
913                 return -EIO;
914             }
915         }
916     /* poll again if fd not ready for IO */
917     } while (!(pfd.revents & (POLLIN | POLLOUT)));
918 
919     return 1;
920 }
921 
pcm_mmap_write(struct pcm * pcm,const void * buffer,unsigned int bytes)922 int pcm_mmap_write(struct pcm *pcm, const void *buffer, unsigned int bytes)
923 {
924     int err = 0, frames, avail;
925     unsigned int offset = 0, count;
926 
927     if (bytes == 0)
928         return 0;
929 
930     count = pcm_bytes_to_frames(pcm, bytes);
931 
932     while (count > 0) {
933 
934         /* get the available space for writing new frames */
935         avail = pcm_avail_update(pcm);
936         if (avail < 0) {
937             fprintf(stderr, "cannot determine available mmap frames");
938             return err;
939         }
940 
941         /* start the audio if we reach the threshold */
942 	    if (!pcm->running &&
943             (pcm->buffer_size - avail) >= pcm->config.start_threshold) {
944             if (pcm_start(pcm) < 0) {
945                fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n",
946                     (unsigned int)pcm->mmap_status->hw_ptr,
947                     (unsigned int)pcm->mmap_control->appl_ptr,
948                     avail);
949                 return -errno;
950             }
951             pcm->wait_for_avail_min = 0;
952         }
953 
954         /* sleep until we have space to write new frames */
955         if (pcm->running) {
956             /* enable waiting for avail_min threshold when less frames than we have to write
957              * are available. */
958             if (!pcm->wait_for_avail_min && (count > (unsigned int)avail))
959                 pcm->wait_for_avail_min = 1;
960 
961             if (pcm->wait_for_avail_min && (avail < pcm->config.avail_min)) {
962                 int time = -1;
963 
964                 /* disable waiting for avail_min threshold to allow small amounts of data to be
965                  * written without waiting as long as there is enough room in buffer. */
966                 pcm->wait_for_avail_min = 0;
967 
968                 if (pcm->flags & PCM_NOIRQ)
969                     time = (pcm->config.avail_min - avail) / pcm->noirq_frames_per_msec;
970 
971                 err = pcm_wait(pcm, time);
972                 if (err < 0) {
973                     pcm->running = 0;
974                     oops(pcm, err, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
975                         (unsigned int)pcm->mmap_status->hw_ptr,
976                         (unsigned int)pcm->mmap_control->appl_ptr,
977                         avail);
978                     pcm->mmap_control->appl_ptr = 0;
979                     return err;
980                 }
981                 continue;
982             }
983         }
984 
985         frames = count;
986         if (frames > avail)
987             frames = avail;
988 
989         if (!frames)
990             break;
991 
992         /* copy frames from buffer */
993         frames = pcm_mmap_write_areas(pcm, buffer, offset, frames);
994         if (frames < 0) {
995             fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
996                     (unsigned int)pcm->mmap_status->hw_ptr,
997                     (unsigned int)pcm->mmap_control->appl_ptr,
998                     avail);
999             return frames;
1000         }
1001 
1002         offset += frames;
1003         count -= frames;
1004     }
1005 
1006     return 0;
1007 }
1008