audio: MMAP NOIRQ mode shareable file descriptor

Derive a shareable file descriptor for the DMA buffer
used for MMAP NOIRQ

Bug: 37167970
Test: run AAudio CTS tests with MMAP enabled
Change-Id: Ibdd633fad35ed397f6de746e5755a02522e39777
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 49a0716..c616221 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -2819,6 +2819,8 @@
     unsigned int offset1;
     unsigned int frames1;
     const char *step = "";
+    uint32_t mmap_size;
+    uint32_t buffer_size;
 
     ALOGV("%s", __func__);
     pthread_mutex_lock(&adev->lock);
@@ -2858,11 +2860,24 @@
         goto exit;
     }
     info->buffer_size_frames = pcm_get_buffer_size(out->pcm);
+    buffer_size = pcm_frames_to_bytes(out->pcm, info->buffer_size_frames);
     info->burst_size_frames = out->config.period_size;
-    info->shared_memory_fd = pcm_get_poll_fd(out->pcm);
-
-    memset(info->shared_memory_address, 0, pcm_frames_to_bytes(out->pcm,
-                                                                info->buffer_size_frames));
+    ret = platform_get_mmap_data_fd(adev->platform,
+                                    out->pcm_device_id, 0 /*playback*/,
+                                    &info->shared_memory_fd,
+                                    &mmap_size);
+    if (ret < 0) {
+        // Fall back to non exclusive mode
+        info->shared_memory_fd = pcm_get_poll_fd(out->pcm);
+    } else {
+        if (mmap_size < buffer_size) {
+            step = "mmap";
+            goto exit;
+        }
+        // FIXME: indicate exclusive mode support by returning a negative buffer size
+        info->buffer_size_frames *= -1;
+    }
+    memset(info->shared_memory_address, 0, buffer_size);
 
     ret = pcm_mmap_commit(out->pcm, 0, MMAP_PERIOD_SIZE);
     if (ret < 0) {
@@ -3356,6 +3371,8 @@
     unsigned int offset1;
     unsigned int frames1;
     const char *step = "";
+    uint32_t mmap_size;
+    uint32_t buffer_size;
 
     pthread_mutex_lock(&adev->lock);
     ALOGV("%s in %p", __func__, in);
@@ -3397,11 +3414,25 @@
         goto exit;
     }
     info->buffer_size_frames = pcm_get_buffer_size(in->pcm);
+    buffer_size = pcm_frames_to_bytes(in->pcm, info->buffer_size_frames);
     info->burst_size_frames = in->config.period_size;
-    info->shared_memory_fd = pcm_get_poll_fd(in->pcm);
+    ret = platform_get_mmap_data_fd(adev->platform,
+                                    in->pcm_device_id, 1 /*capture*/,
+                                    &info->shared_memory_fd,
+                                    &mmap_size);
+    if (ret < 0) {
+        // Fall back to non exclusive mode
+        info->shared_memory_fd = pcm_get_poll_fd(in->pcm);
+    } else {
+        if (mmap_size < buffer_size) {
+            step = "mmap";
+            goto exit;
+        }
+        // FIXME: indicate exclusive mode support by returning a negative buffer size
+        info->buffer_size_frames *= -1;
+    }
 
-    memset(info->shared_memory_address, 0, pcm_frames_to_bytes(in->pcm,
-                                                                info->buffer_size_frames));
+    memset(info->shared_memory_address, 0, buffer_size);
 
     ret = pcm_mmap_commit(in->pcm, 0, MMAP_PERIOD_SIZE);
     if (ret < 0) {
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 32c80c0..e5bc111 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -2365,3 +2365,9 @@
     }
     return 0;
 }
+
+int platform_get_mmap_data_fd(void *platform __unused, int fe_dev __unused, int dir __unused,
+                              int *fd __unused, uint32_t *size __unused)
+{
+    return -ENOSYS;
+}
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 53838a7..50c6490 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1200,3 +1200,9 @@
     }
     return 0;
 }
+
+int platform_get_mmap_data_fd(void *platform __unused, int fe_dev __unused, int dir __unused,
+                              int *fd __unused, uint32_t *size __unused)
+{
+    return -ENOSYS;
+}
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 1b0bc20..459a5d4 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -28,6 +28,9 @@
 #include "platform.h"
 #include "audio_extn.h"
 #include <linux/msm_audio.h>
+#if defined (PLATFORM_MSM8996) || (PLATFORM_MSM8998)
+#include <sound/devdep_params.h>
+#endif
 
 #define MIXER_XML_DEFAULT_PATH "mixer_paths.xml"
 #define MIXER_XML_BASE_STRING "mixer_paths"
@@ -4037,3 +4040,35 @@
     }
     return 0;
 }
+
+int platform_get_mmap_data_fd(void *platform __unused, int fe_dev __unused, int dir __unused,
+                              int *fd __unused, uint32_t *size __unused)
+{
+#if defined (PLATFORM_MSM8996) || (PLATFORM_MSM8998)
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    int hw_fd = -1;
+    char dev_name[128];
+    struct snd_pcm_mmap_fd mmap_fd;
+    memset(&mmap_fd, 0, sizeof(mmap_fd));
+    mmap_fd.dir = dir;
+    snprintf(dev_name, sizeof(dev_name), "/dev/snd/hwC%uD%u",
+             adev->snd_card, HWDEP_FE_BASE+fe_dev);
+    hw_fd = open(dev_name, O_RDONLY);
+    if (hw_fd < 0) {
+        ALOGE("fe hw dep node open %d/%d failed", adev->snd_card, fe_dev);
+        return -1;
+    }
+    if (ioctl(hw_fd, SNDRV_PCM_IOCTL_MMAP_DATA_FD, &mmap_fd) < 0) {
+        ALOGE("fe hw dep node ioctl failed");
+        close(hw_fd);
+        return -1;
+    }
+    *fd = mmap_fd.fd;
+    *size = mmap_fd.size;
+    close(hw_fd); // mmap_fd should still be valid
+    return 0;
+#else
+    return -1;
+#endif
+}
diff --git a/hal/platform_api.h b/hal/platform_api.h
index bca3e2b..341afb2 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -145,4 +145,6 @@
 int platform_set_sidetone(struct audio_device *adev,
                           snd_device_t out_snd_device,
                           bool enable, char * str);
+int platform_get_mmap_data_fd(void *platform, int dev, int dir,
+                              int *fd, uint32_t *size);
 #endif // AUDIO_PLATFORM_API_H