]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/platform-external-tinyalsa.git/blobdiff - pcm.c
Merge "Revert "Update 64-bit blacklist for tinyalsa""
[android-sdk/platform-external-tinyalsa.git] / pcm.c
diff --git a/pcm.c b/pcm.c
index d12e5daa2147b15b2d23836036a6256040dca6a1..450177786550ddc66ec1e20d9c2d20e826f24ec2 100644 (file)
--- a/pcm.c
+++ b/pcm.c
@@ -93,12 +93,22 @@ static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
     }
 }
 
-static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned int val)
+static unsigned int param_get_min(struct snd_pcm_hw_params *p, int n)
 {
     if (param_is_interval(n)) {
         struct snd_interval *i = param_to_interval(p, n);
-        i->max = val;
+        return i->min;
+    }
+    return 0;
+}
+
+static unsigned int param_get_max(struct snd_pcm_hw_params *p, int n)
+{
+    if (param_is_interval(n)) {
+        struct snd_interval *i = param_to_interval(p, n);
+        return i->max;
     }
+    return 0;
 }
 
 static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
@@ -138,6 +148,9 @@ static void param_init(struct snd_pcm_hw_params *p)
             i->min = 0;
             i->max = ~0;
     }
+    p->rmask = ~0U;
+    p->cmask = 0;
+    p->info = ~0U;
 }
 
 #define PCM_ERROR_MAX 128
@@ -156,6 +169,7 @@ struct pcm {
     struct snd_pcm_sync_ptr *sync_ptr;
     void *mmap_buffer;
     unsigned int noirq_frames_per_msec;
+    int wait_for_avail_min;
 };
 
 unsigned int pcm_get_buffer_size(struct pcm *pcm)
@@ -189,16 +203,21 @@ static unsigned int pcm_format_to_alsa(enum pcm_format format)
     switch (format) {
     case PCM_FORMAT_S32_LE:
         return SNDRV_PCM_FORMAT_S32_LE;
+    case PCM_FORMAT_S8:
+        return SNDRV_PCM_FORMAT_S8;
+    case PCM_FORMAT_S24_LE:
+        return SNDRV_PCM_FORMAT_S24_LE;
     default:
     case PCM_FORMAT_S16_LE:
         return SNDRV_PCM_FORMAT_S16_LE;
     };
 }
 
-static unsigned int pcm_format_to_bits(enum pcm_format format)
+unsigned int pcm_format_to_bits(enum pcm_format format)
 {
     switch (format) {
     case PCM_FORMAT_S32_LE:
+    case PCM_FORMAT_S24_LE:
         return 32;
     default:
     case PCM_FORMAT_S16_LE:
@@ -249,7 +268,10 @@ static int pcm_hw_mmap_status(struct pcm *pcm) {
         pcm->mmap_status = NULL;
         goto mmap_error;
     }
-    pcm->mmap_control->avail_min = 1;
+    if (pcm->flags & PCM_MMAP)
+        pcm->mmap_control->avail_min = pcm->config.avail_min;
+    else
+        pcm->mmap_control->avail_min = 1;
 
     return 0;
 
@@ -260,7 +282,11 @@ mmap_error:
         return -ENOMEM;
     pcm->mmap_status = &pcm->sync_ptr->s.status;
     pcm->mmap_control = &pcm->sync_ptr->c.control;
-    pcm->mmap_control->avail_min = 1;
+    if (pcm->flags & PCM_MMAP)
+        pcm->mmap_control->avail_min = pcm->config.avail_min;
+    else
+        pcm->mmap_control->avail_min = 1;
+
     pcm_sync_ptr(pcm, 0);
 
     return 0;
@@ -282,7 +308,7 @@ static void pcm_hw_munmap_status(struct pcm *pcm) {
 }
 
 static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
-                          const char *src, unsigned int src_offset,
+                          char *buf, unsigned int src_offset,
                           unsigned int frames)
 {
     int size_bytes = pcm_frames_to_bytes(pcm, frames);
@@ -290,12 +316,18 @@ static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
     int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
 
     /* interleaved only atm */
-    memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
-           src + src_offset_bytes, size_bytes);
+    if (pcm->flags & PCM_IN)
+        memcpy(buf + src_offset_bytes,
+               (char*)pcm->mmap_buffer + pcm_offset_bytes,
+               size_bytes);
+    else
+        memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
+               buf + src_offset_bytes,
+               size_bytes);
     return 0;
 }
 
-static int pcm_mmap_write_areas(struct pcm *pcm, char *src,
+static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf,
                                 unsigned int offset, unsigned int size)
 {
     void *pcm_areas;
@@ -305,7 +337,7 @@ static int pcm_mmap_write_areas(struct pcm *pcm, char *src,
     while (size > 0) {
         frames = size;
         pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
-        pcm_areas_copy(pcm, pcm_offset, src, offset, frames);
+        pcm_areas_copy(pcm, pcm_offset, buf, offset, frames);
         commit = pcm_mmap_commit(pcm, pcm_offset, frames);
         if (commit < 0) {
             oops(pcm, commit, "failed to commit %d frames\n", frames);
@@ -333,6 +365,10 @@ int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
     if (rc < 0)
         return -1;
 
+    if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
+            (pcm->mmap_status->state != PCM_STATE_DRAINING))
+        return -1;
+
     *tstamp = pcm->mmap_status->tstamp;
     if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
         return -1;
@@ -344,21 +380,23 @@ int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
         frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
 
     if (frames < 0)
-        return -1;
+        frames += pcm->boundary;
+    else if (frames > (int)pcm->boundary)
+        frames -= pcm->boundary;
 
     *avail = (unsigned int)frames;
 
     return 0;
 }
 
-int pcm_write(struct pcm *pcm, void *data, unsigned int count)
+int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
 {
     struct snd_xferi x;
 
     if (pcm->flags & PCM_IN)
         return -EINVAL;
 
-    x.buf = data;
+    x.buf = (void*)data;
     x.frames = count / (pcm->config.channels *
                         pcm_format_to_bits(pcm->config.format) / 8);
 
@@ -374,8 +412,12 @@ int pcm_write(struct pcm *pcm, void *data, unsigned int count)
         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
             pcm->running = 0;
             if (errno == EPIPE) {
-                    /* we failed to make our window -- try to restart */
+                /* we failed to make our window -- try to restart if we are
+                 * allowed to do so.  Otherwise, simply allow the EPIPE error to
+                 * propagate up to the app level */
                 pcm->underruns++;
+                if (pcm->flags & PCM_NORESTART)
+                    return -EPIPE;
                 continue;
             }
             return oops(pcm, errno, "cannot write stream data");
@@ -397,11 +439,10 @@ int pcm_read(struct pcm *pcm, void *data, unsigned int count)
 
     for (;;) {
         if (!pcm->running) {
-            if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))
-                return oops(pcm, errno, "cannot prepare channel");
-            if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START))
-                return oops(pcm, errno, "cannot start channel");
-            pcm->running = 1;
+            if (pcm_start(pcm) < 0) {
+                fprintf(stderr, "start error");
+                return -errno;
+            }
         }
         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
             pcm->running = 0;
@@ -420,6 +461,129 @@ static struct pcm bad_pcm = {
     .fd = -1,
 };
 
+struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
+                                  unsigned int flags)
+{
+    struct snd_pcm_hw_params *params;
+    char fn[256];
+    int fd;
+
+    snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
+             flags & PCM_IN ? 'c' : 'p');
+
+    fd = open(fn, O_RDWR);
+    if (fd < 0) {
+        fprintf(stderr, "cannot open device '%s'\n", fn);
+        goto err_open;
+    }
+
+    params = calloc(1, sizeof(struct snd_pcm_hw_params));
+    if (!params)
+        goto err_calloc;
+
+    param_init(params);
+    if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
+        fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
+        goto err_hw_refine;
+    }
+
+    close(fd);
+
+    return (struct pcm_params *)params;
+
+err_hw_refine:
+    free(params);
+err_calloc:
+    close(fd);
+err_open:
+    return NULL;
+}
+
+void pcm_params_free(struct pcm_params *pcm_params)
+{
+    struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+
+    if (params)
+        free(params);
+}
+
+static int pcm_param_to_alsa(enum pcm_param param)
+{
+    switch (param) {
+    case PCM_PARAM_SAMPLE_BITS:
+        return SNDRV_PCM_HW_PARAM_SAMPLE_BITS;
+        break;
+    case PCM_PARAM_FRAME_BITS:
+        return SNDRV_PCM_HW_PARAM_FRAME_BITS;
+        break;
+    case PCM_PARAM_CHANNELS:
+        return SNDRV_PCM_HW_PARAM_CHANNELS;
+        break;
+    case PCM_PARAM_RATE:
+        return SNDRV_PCM_HW_PARAM_RATE;
+        break;
+    case PCM_PARAM_PERIOD_TIME:
+        return SNDRV_PCM_HW_PARAM_PERIOD_TIME;
+        break;
+    case PCM_PARAM_PERIOD_SIZE:
+        return SNDRV_PCM_HW_PARAM_PERIOD_SIZE;
+        break;
+    case PCM_PARAM_PERIOD_BYTES:
+        return SNDRV_PCM_HW_PARAM_PERIOD_BYTES;
+        break;
+    case PCM_PARAM_PERIODS:
+        return SNDRV_PCM_HW_PARAM_PERIODS;
+        break;
+    case PCM_PARAM_BUFFER_TIME:
+        return SNDRV_PCM_HW_PARAM_BUFFER_TIME;
+        break;
+    case PCM_PARAM_BUFFER_SIZE:
+        return SNDRV_PCM_HW_PARAM_BUFFER_SIZE;
+        break;
+    case PCM_PARAM_BUFFER_BYTES:
+        return SNDRV_PCM_HW_PARAM_BUFFER_BYTES;
+        break;
+    case PCM_PARAM_TICK_TIME:
+        return SNDRV_PCM_HW_PARAM_TICK_TIME;
+        break;
+
+    default:
+        return -1;
+    }
+}
+
+unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
+                                enum pcm_param param)
+{
+    struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+    int p;
+
+    if (!params)
+        return 0;
+
+    p = pcm_param_to_alsa(param);
+    if (p < 0)
+        return 0;
+
+    return param_get_min(params, p);
+}
+
+unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
+                                enum pcm_param param)
+{
+    struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+    int p;
+
+    if (!params)
+        return 0;
+
+    p = pcm_param_to_alsa(param);
+    if (p < 0)
+        return 0;
+
+    return param_get_max(params, p);
+}
+
 int pcm_close(struct pcm *pcm)
 {
     if (pcm == &bad_pcm)
@@ -529,27 +693,42 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
     memset(&sparams, 0, sizeof(sparams));
     sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
     sparams.period_step = 1;
-    sparams.avail_min = 1;
 
-    if (!config->start_threshold)
-        pcm->config.start_threshold = sparams.start_threshold =
-            config->period_count * config->period_size / 2;
-    else
+    if (!config->start_threshold) {
+        if (pcm->flags & PCM_IN)
+            pcm->config.start_threshold = sparams.start_threshold = 1;
+        else
+            pcm->config.start_threshold = sparams.start_threshold =
+                config->period_count * config->period_size / 2;
+    } else
         sparams.start_threshold = config->start_threshold;
 
     /* pick a high stop threshold - todo: does this need further tuning */
-    if (!config->stop_threshold)
-        pcm->config.stop_threshold = sparams.stop_threshold =
-            config->period_count * config->period_size * 10;
+    if (!config->stop_threshold) {
+        if (pcm->flags & PCM_IN)
+            pcm->config.stop_threshold = sparams.stop_threshold =
+                config->period_count * config->period_size * 10;
+        else
+            pcm->config.stop_threshold = sparams.stop_threshold =
+                config->period_count * config->period_size;
+    }
     else
         sparams.stop_threshold = config->stop_threshold;
 
+    if (!pcm->config.avail_min) {
+        if (pcm->flags & PCM_MMAP)
+            pcm->config.avail_min = sparams.avail_min = pcm->config.period_size;
+        else
+            pcm->config.avail_min = sparams.avail_min = 1;
+    } else
+        sparams.avail_min = config->avail_min;
+
     sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
     sparams.silence_size = 0;
     sparams.silence_threshold = config->silence_threshold;
     pcm->boundary = sparams.boundary = pcm->buffer_size;
 
-    while (pcm->boundary * 2 <= LONG_MAX - pcm->buffer_size)
+    while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
                pcm->boundary *= 2;
 
     if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
@@ -563,6 +742,17 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
         goto fail;
     }
 
+#ifdef SNDRV_PCM_IOCTL_TTSTAMP
+    if (pcm->flags & PCM_MONOTONIC) {
+        int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
+        rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
+        if (rc < 0) {
+            oops(pcm, rc, "cannot set timestamp type");
+            goto fail;
+        }
+    }
+#endif
+
     pcm->underruns = 0;
     return pcm;
 
@@ -697,10 +887,18 @@ int pcm_state(struct pcm *pcm)
     return pcm->mmap_status->state;
 }
 
+int pcm_set_avail_min(struct pcm *pcm, int avail_min)
+{
+    if ((~pcm->flags) & (PCM_MMAP | PCM_NOIRQ))
+        return -ENOSYS;
+
+    pcm->config.avail_min = avail_min;
+    return 0;
+}
+
 int pcm_wait(struct pcm *pcm, int timeout)
 {
     struct pollfd pfd;
-    unsigned short revents = 0;
     int err;
 
     pfd.fd = pcm->fd;
@@ -739,7 +937,7 @@ int pcm_wait(struct pcm *pcm, int timeout)
     return 1;
 }
 
-int pcm_mmap_write(struct pcm *pcm, void *buffer, unsigned int bytes)
+int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes)
 {
     int err = 0, frames, avail;
     unsigned int offset = 0, count;
@@ -768,28 +966,38 @@ int pcm_mmap_write(struct pcm *pcm, void *buffer, unsigned int bytes)
                     avail);
                 return -errno;
             }
+            pcm->wait_for_avail_min = 0;
         }
 
         /* sleep until we have space to write new frames */
-        if (pcm->running &&
-            (unsigned int)avail < pcm->mmap_control->avail_min) {
-            int time = -1;
-
-            if (pcm->flags & PCM_NOIRQ)
-                time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min)
-                        / pcm->noirq_frames_per_msec;
-
-            err = pcm_wait(pcm, time);
-            if (err < 0) {
-                pcm->running = 0;
-                fprintf(stderr, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
-                    (unsigned int)pcm->mmap_status->hw_ptr,
-                    (unsigned int)pcm->mmap_control->appl_ptr,
-                    avail);
-                pcm->mmap_control->appl_ptr = 0;
-                return err;
+        if (pcm->running) {
+            /* enable waiting for avail_min threshold when less frames than we have to write
+             * are available. */
+            if (!pcm->wait_for_avail_min && (count > (unsigned int)avail))
+                pcm->wait_for_avail_min = 1;
+
+            if (pcm->wait_for_avail_min && (avail < pcm->config.avail_min)) {
+                int time = -1;
+
+                /* disable waiting for avail_min threshold to allow small amounts of data to be
+                 * written without waiting as long as there is enough room in buffer. */
+                pcm->wait_for_avail_min = 0;
+
+                if (pcm->flags & PCM_NOIRQ)
+                    time = (pcm->config.avail_min - avail) / pcm->noirq_frames_per_msec;
+
+                err = pcm_wait(pcm, time);
+                if (err < 0) {
+                    pcm->running = 0;
+                    oops(pcm, err, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
+                        (unsigned int)pcm->mmap_status->hw_ptr,
+                        (unsigned int)pcm->mmap_control->appl_ptr,
+                        avail);
+                    pcm->mmap_control->appl_ptr = 0;
+                    return err;
+                }
+                continue;
             }
-            continue;
         }
 
         frames = count;
@@ -800,7 +1008,7 @@ int pcm_mmap_write(struct pcm *pcm, void *buffer, unsigned int bytes)
             break;
 
         /* copy frames from buffer */
-        frames = pcm_mmap_write_areas(pcm, buffer, offset, frames);
+        frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames);
         if (frames < 0) {
             fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
                     (unsigned int)pcm->mmap_status->hw_ptr,
@@ -813,6 +1021,21 @@ int pcm_mmap_write(struct pcm *pcm, void *buffer, unsigned int bytes)
         count -= frames;
     }
 
-_end:
     return 0;
 }
+
+int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count)
+{
+    if ((~pcm->flags) & (PCM_OUT | PCM_MMAP))
+        return -ENOSYS;
+
+    return pcm_mmap_transfer(pcm, (void *)data, count);
+}
+
+int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count)
+{
+    if ((~pcm->flags) & (PCM_IN | PCM_MMAP))
+        return -ENOSYS;
+
+    return pcm_mmap_transfer(pcm, data, count);
+}