alsa_api_test: Add the option to test filling mmap buffer

Test the API to fill whole buffer with zeros. The sequence is:
1. Use snd_pcm_mmap_begin to get the mmap buffer.
2. Fill the mmap buffer with zeros using memset.
3. Do not call snd_pcm_mmap_commit.
4. Let device play for some periods.
5. Use snd_pcm_mmap_begin to get the mmap buffer.
6. Check mmap buffer contains all zeros.

BUG=chromium:601346
TEST=run `alsa_api_test --device hw:1,0 --fill` on samus.

Change-Id: Iea92341f0fe3d1094ea4311198b768bbaaf7cdbf
Reviewed-on: https://chromium-review.googlesource.com/341842
Commit-Ready: Cheng-Yi Chiang <cychiang@chromium.org>
Tested-by: Cheng-Yi Chiang <cychiang@chromium.org>
Reviewed-by: Hsinyu Chao <hychao@chromium.org>
diff --git a/alsa_api_test.c b/alsa_api_test.c
index 55a4740..2122de9 100644
--- a/alsa_api_test.c
+++ b/alsa_api_test.c
@@ -22,14 +22,18 @@
 static snd_pcm_uframes_t period_size = 240;
 
 /* Fill frames of zeros. */
-static void pcm_fill_zeros(snd_pcm_t *handle, snd_pcm_uframes_t frames) {
-    short *play_buf;
-    int err;
+static void pcm_fill(snd_pcm_t *handle, snd_pcm_uframes_t frames,
+                     int16_t value) {
+    int16_t *play_buf;
+    int err, i;
 
     play_buf = calloc(frames * channels, sizeof(play_buf[0]));
-    printf("Write %d into device\n", (int)frames);
+    for (i = 0; i < frames * channels; i++)
+        play_buf[i] = value;
 
-    if ((err = snd_pcm_writei(handle, play_buf, frames))
+    printf("Write %d of value %d into device\n", (int)frames, (int)value);
+
+    if ((err = snd_pcm_mmap_writei(handle, play_buf, frames))
          != frames) {
         fprintf(stderr, "write to audio interface failed (%s)\n",
                 snd_strerror(err));
@@ -55,7 +59,7 @@
     }
 
     if ((err = snd_pcm_hw_params_set_access(handle, hw_params,
-            SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+            SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
         fprintf(stderr, "cannot set access type (%s)\n",
                 snd_strerror(err));
         exit(1);
@@ -223,7 +227,7 @@
 
     pcm_init(handle);
 
-    pcm_fill_zeros(handle, buffer_frames);
+    pcm_fill(handle, buffer_frames, 0);
 
     wait_for_periods(handle, wait_periods);
 
@@ -255,7 +259,7 @@
     check_hw_level_in_range(hw_level, 0, fuzz);
 
     /* Fills some zeros after moving to make sure PCM still plays fine. */
-    pcm_fill_zeros(handle, period_size * periods_after_move);
+    pcm_fill(handle, period_size * periods_after_move, 0);
     hw_level = buffer_frames - snd_pcm_avail(handle);
     printf("hw_level after filling %d period is %d\n",
            periods_after_move, (int)hw_level);
@@ -289,7 +293,7 @@
 
     pcm_init(handle);
 
-    pcm_fill_zeros(handle, buffer_frames);
+    pcm_fill(handle, buffer_frames, 0);
 
     wait_for_periods(handle, wait_periods);
 
@@ -339,9 +343,70 @@
     }
 }
 
+void alsa_fill_test()
+{
+    int err;
+    snd_pcm_t *handle;
+    unsigned int wait_periods = 10;
+    const snd_pcm_channel_area_t *my_areas;
+    snd_pcm_uframes_t offset, frames;
+    int16_t *dst, *zeros;
+    int n_bytes;
+
+    if ((err = snd_pcm_open(&handle, play_dev,
+                SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
+        fprintf(stderr, "cannot open audio device %s (%s)\n",
+                play_dev, snd_strerror(err));
+        exit(1);
+    }
+
+    pcm_init(handle);
+
+    /* Write nonzero values into buffer. */
+    pcm_fill(handle, buffer_frames, 1);
+
+    /* Play for some periods. */
+    wait_for_periods(handle, wait_periods);
+
+    /* Get the mmap area. */
+    err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
+    if (err < 0) {
+        fprintf(stderr, "cannot mmap begin (%s)\n", snd_strerror(err));
+        exit(1);
+    }
+
+    /* Fill whole buffer with zeros without committing it.
+     * The number of bytes is buffer_frames * channel * 2 (16 bit sample) */
+    n_bytes = buffer_frames * channels * 2;
+    memset((int8_t *)my_areas[0].addr, 0, n_bytes);
+    printf("Filled mmap buffer with zeros\n");
+
+    /* Play for some periods. */
+    wait_for_periods(handle, wait_periods);
+
+    /* Check the samples in buffer are all zeros. */
+    err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
+    if (err < 0) {
+        fprintf(stderr, "cannot mmap begin the second time (%s)\n",
+                snd_strerror(err));
+        exit(1);
+    }
+    dst = (int16_t *)my_areas[0].addr;
+
+    zeros = calloc(buffer_frames * channels, sizeof(zeros[0]));
+
+    if (memcmp(zeros, dst, n_bytes)) {
+        fprintf(stderr, "buffer is not all zeros\n");
+        free(zeros);
+        exit(1);
+    }
+    free(zeros);
+    printf("Buffer is filled with zeros\n");
+}
+
 int main(int argc, char *argv[])
 {
-    int c, drop_test = 0, move_test = 0;
+    int c, drop_test = 0, move_test = 0, fill_test = 0;
     const char *short_opt = "hd:rm";
     struct option long_opt[] =
     {
@@ -349,6 +414,7 @@
        {"device",        required_argument, NULL, 'd'},
        {"drop",          no_argument,       NULL, 'r'},
        {"move",          no_argument,       NULL, 'm'},
+       {"fill",          no_argument,       NULL, 'f'},
        {NULL,            0,                 NULL, 0  }
     };
 
@@ -372,12 +438,18 @@
            printf("Test snd_pcm_forward\n");
            break;
 
+       case 'f':
+           fill_test = 1;
+           printf("Test snd_pcm_mmap_begin and filling buffer.\n");
+           break;
+
        case 'h':
            printf("Usage: %s [OPTIONS]\n", argv[0]);
            printf("  --device <Device>       Device, default to hw:0,0\n");
            printf("  -h, --help              Print this help and exit\n");
            printf("  --drop                  Test snd_pcm_drop\n");
            printf("  --move                  Test snd_pcm_forward\n");
+           printf("  --fill                  Test snd_pcm_mmap_begin\n");
            printf("\n");
            return(0);
            break;
@@ -406,5 +478,10 @@
         exit(0);
     }
 
+    if (fill_test) {
+        alsa_fill_test();
+        exit(0);
+    }
+
     exit(0);
 }