Home | History | Annotate | Download | only in tinyalsa
      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 
     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 
     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 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    175 unsigned int pcm_get_buffer_size(struct pcm *pcm)
    176 {
    177     return pcm->buffer_size;
    178 }
    179 
    180 const char* pcm_get_error(struct pcm *pcm)
    181 {
    182     return pcm->error;
    183 }
    184 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    750 int pcm_is_ready(struct pcm *pcm)
    751 {
    752     return pcm->fd >= 0;
    753 }
    754 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    857 int pcm_avail_update(struct pcm *pcm)
    858 {
    859     pcm_sync_ptr(pcm, 0);
    860     return pcm_mmap_avail(pcm);
    861 }
    862 
    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 
    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 
    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 
    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