Merge branch 'for-linus' into for-next
[platform/kernel/linux-starfive.git] / sound / core / pcm_native.c
index d083225..c375c41 100644 (file)
@@ -4,6 +4,7 @@
  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  */
 
+#include <linux/compat.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/file.h>
@@ -895,8 +896,8 @@ snd_pcm_calc_delay(struct snd_pcm_substream *substream)
        return delay + substream->runtime->delay;
 }
 
-int snd_pcm_status(struct snd_pcm_substream *substream,
-                  struct snd_pcm_status *status)
+int snd_pcm_status64(struct snd_pcm_substream *substream,
+                    struct snd_pcm_status64 *status)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
 
@@ -922,14 +923,22 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
        status->suspended_state = runtime->status->suspended_state;
        if (status->state == SNDRV_PCM_STATE_OPEN)
                goto _end;
-       status->trigger_tstamp = runtime->trigger_tstamp;
+       status->trigger_tstamp_sec = runtime->trigger_tstamp.tv_sec;
+       status->trigger_tstamp_nsec = runtime->trigger_tstamp.tv_nsec;
        if (snd_pcm_running(substream)) {
                snd_pcm_update_hw_ptr(substream);
                if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
-                       status->tstamp = runtime->status->tstamp;
-                       status->driver_tstamp = runtime->driver_tstamp;
-                       status->audio_tstamp =
-                               runtime->status->audio_tstamp;
+                       status->tstamp_sec = runtime->status->tstamp.tv_sec;
+                       status->tstamp_nsec =
+                               runtime->status->tstamp.tv_nsec;
+                       status->driver_tstamp_sec =
+                               runtime->driver_tstamp.tv_sec;
+                       status->driver_tstamp_nsec =
+                               runtime->driver_tstamp.tv_nsec;
+                       status->audio_tstamp_sec =
+                               runtime->status->audio_tstamp.tv_sec;
+                       status->audio_tstamp_nsec =
+                               runtime->status->audio_tstamp.tv_nsec;
                        if (runtime->audio_tstamp_report.valid == 1)
                                /* backwards compatibility, no report provided in COMPAT mode */
                                snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data,
@@ -940,8 +949,13 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
                }
        } else {
                /* get tstamp only in fallback mode and only if enabled */
-               if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
-                       snd_pcm_gettime(runtime, &status->tstamp);
+               if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+                       struct timespec64 tstamp;
+
+                       snd_pcm_gettime(runtime, &tstamp);
+                       status->tstamp_sec = tstamp.tv_sec;
+                       status->tstamp_nsec = tstamp.tv_nsec;
+               }
        }
  _tstamp_end:
        status->appl_ptr = runtime->control->appl_ptr;
@@ -958,11 +972,11 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int snd_pcm_status_user(struct snd_pcm_substream *substream,
-                              struct snd_pcm_status __user * _status,
-                              bool ext)
+static int snd_pcm_status_user64(struct snd_pcm_substream *substream,
+                                struct snd_pcm_status64 __user * _status,
+                                bool ext)
 {
-       struct snd_pcm_status status;
+       struct snd_pcm_status64 status;
        int res;
 
        memset(&status, 0, sizeof(status));
@@ -974,7 +988,7 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream,
        if (ext && get_user(status.audio_tstamp_data,
                                (u32 __user *)(&_status->audio_tstamp_data)))
                return -EFAULT;
-       res = snd_pcm_status(substream, &status);
+       res = snd_pcm_status64(substream, &status);
        if (res < 0)
                return res;
        if (copy_to_user(_status, &status, sizeof(status)))
@@ -982,6 +996,55 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream,
        return 0;
 }
 
+static int snd_pcm_status_user32(struct snd_pcm_substream *substream,
+                                struct snd_pcm_status32 __user * _status,
+                                bool ext)
+{
+       struct snd_pcm_status64 status64;
+       struct snd_pcm_status32 status32;
+       int res;
+
+       memset(&status64, 0, sizeof(status64));
+       memset(&status32, 0, sizeof(status32));
+       /*
+        * with extension, parameters are read/write,
+        * get audio_tstamp_data from user,
+        * ignore rest of status structure
+        */
+       if (ext && get_user(status64.audio_tstamp_data,
+                           (u32 __user *)(&_status->audio_tstamp_data)))
+               return -EFAULT;
+       res = snd_pcm_status64(substream, &status64);
+       if (res < 0)
+               return res;
+
+       status32 = (struct snd_pcm_status32) {
+               .state = status64.state,
+               .trigger_tstamp_sec = status64.trigger_tstamp_sec,
+               .trigger_tstamp_nsec = status64.trigger_tstamp_nsec,
+               .tstamp_sec = status64.tstamp_sec,
+               .tstamp_nsec = status64.tstamp_nsec,
+               .appl_ptr = status64.appl_ptr,
+               .hw_ptr = status64.hw_ptr,
+               .delay = status64.delay,
+               .avail = status64.avail,
+               .avail_max = status64.avail_max,
+               .overrange = status64.overrange,
+               .suspended_state = status64.suspended_state,
+               .audio_tstamp_data = status64.audio_tstamp_data,
+               .audio_tstamp_sec = status64.audio_tstamp_sec,
+               .audio_tstamp_nsec = status64.audio_tstamp_nsec,
+               .driver_tstamp_sec = status64.audio_tstamp_sec,
+               .driver_tstamp_nsec = status64.audio_tstamp_nsec,
+               .audio_tstamp_accuracy = status64.audio_tstamp_accuracy,
+       };
+
+       if (copy_to_user(_status, &status32, sizeof(status32)))
+               return -EFAULT;
+
+       return 0;
+}
+
 static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
                                struct snd_pcm_channel_info * info)
 {
@@ -2830,6 +2893,107 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
        return 0;
 }
 
+struct snd_pcm_mmap_status32 {
+       s32 state;
+       s32 pad1;
+       u32 hw_ptr;
+       s32 tstamp_sec;
+       s32 tstamp_nsec;
+       s32 suspended_state;
+       s32 audio_tstamp_sec;
+       s32 audio_tstamp_nsec;
+} __attribute__((packed));
+
+struct snd_pcm_mmap_control32 {
+       u32 appl_ptr;
+       u32 avail_min;
+};
+
+struct snd_pcm_sync_ptr32 {
+       u32 flags;
+       union {
+               struct snd_pcm_mmap_status32 status;
+               unsigned char reserved[64];
+       } s;
+       union {
+               struct snd_pcm_mmap_control32 control;
+               unsigned char reserved[64];
+       } c;
+} __attribute__((packed));
+
+/* recalcuate the boundary within 32bit */
+static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime)
+{
+       snd_pcm_uframes_t boundary;
+
+       if (! runtime->buffer_size)
+               return 0;
+       boundary = runtime->buffer_size;
+       while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
+               boundary *= 2;
+       return boundary;
+}
+
+static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
+                                        struct snd_pcm_sync_ptr32 __user *src)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       volatile struct snd_pcm_mmap_status *status;
+       volatile struct snd_pcm_mmap_control *control;
+       u32 sflags;
+       struct snd_pcm_mmap_control scontrol;
+       struct snd_pcm_mmap_status sstatus;
+       snd_pcm_uframes_t boundary;
+       int err;
+
+       if (snd_BUG_ON(!runtime))
+               return -EINVAL;
+
+       if (get_user(sflags, &src->flags) ||
+           get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
+           get_user(scontrol.avail_min, &src->c.control.avail_min))
+               return -EFAULT;
+       if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+               err = snd_pcm_hwsync(substream);
+               if (err < 0)
+                       return err;
+       }
+       status = runtime->status;
+       control = runtime->control;
+       boundary = recalculate_boundary(runtime);
+       if (! boundary)
+               boundary = 0x7fffffff;
+       snd_pcm_stream_lock_irq(substream);
+       /* FIXME: we should consider the boundary for the sync from app */
+       if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+               control->appl_ptr = scontrol.appl_ptr;
+       else
+               scontrol.appl_ptr = control->appl_ptr % boundary;
+       if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+               control->avail_min = scontrol.avail_min;
+       else
+               scontrol.avail_min = control->avail_min;
+       sstatus.state = status->state;
+       sstatus.hw_ptr = status->hw_ptr % boundary;
+       sstatus.tstamp = status->tstamp;
+       sstatus.suspended_state = status->suspended_state;
+       sstatus.audio_tstamp = status->audio_tstamp;
+       snd_pcm_stream_unlock_irq(substream);
+       if (put_user(sstatus.state, &src->s.status.state) ||
+           put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
+           put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
+           put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) ||
+           put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
+           put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) ||
+           put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) ||
+           put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
+           put_user(scontrol.avail_min, &src->c.control.avail_min))
+               return -EFAULT;
+
+       return 0;
+}
+#define __SNDRV_PCM_IOCTL_SYNC_PTR32 _IOWR('A', 0x23, struct snd_pcm_sync_ptr32)
+
 static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
@@ -2959,10 +3123,14 @@ static int snd_pcm_common_ioctl(struct file *file,
                return snd_pcm_hw_free(substream);
        case SNDRV_PCM_IOCTL_SW_PARAMS:
                return snd_pcm_sw_params_user(substream, arg);
-       case SNDRV_PCM_IOCTL_STATUS:
-               return snd_pcm_status_user(substream, arg, false);
-       case SNDRV_PCM_IOCTL_STATUS_EXT:
-               return snd_pcm_status_user(substream, arg, true);
+       case SNDRV_PCM_IOCTL_STATUS32:
+               return snd_pcm_status_user32(substream, arg, false);
+       case SNDRV_PCM_IOCTL_STATUS_EXT32:
+               return snd_pcm_status_user32(substream, arg, true);
+       case SNDRV_PCM_IOCTL_STATUS64:
+               return snd_pcm_status_user64(substream, arg, false);
+       case SNDRV_PCM_IOCTL_STATUS_EXT64:
+               return snd_pcm_status_user64(substream, arg, true);
        case SNDRV_PCM_IOCTL_CHANNEL_INFO:
                return snd_pcm_channel_info_user(substream, arg);
        case SNDRV_PCM_IOCTL_PREPARE:
@@ -2994,7 +3162,9 @@ static int snd_pcm_common_ioctl(struct file *file,
                        return -EFAULT;
                return 0;
        }
-       case SNDRV_PCM_IOCTL_SYNC_PTR:
+       case __SNDRV_PCM_IOCTL_SYNC_PTR32:
+               return snd_pcm_ioctl_sync_ptr_compat(substream, arg);
+       case __SNDRV_PCM_IOCTL_SYNC_PTR64:
                return snd_pcm_sync_ptr(substream, arg);
 #ifdef CONFIG_SND_SUPPORT_OLD_API
        case SNDRV_PCM_IOCTL_HW_REFINE_OLD:
@@ -3332,8 +3502,6 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
 
 static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file)
 {
-       if (pcm_file->no_compat_mmap)
-               return false;
        /* See pcm_control_mmap_allowed() below.
         * Since older alsa-lib requires both status and control mmaps to be
         * coupled, we have to disable the status mmap for old alsa-lib, too.
@@ -3558,11 +3726,19 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
 
        offset = area->vm_pgoff << PAGE_SHIFT;
        switch (offset) {
-       case SNDRV_PCM_MMAP_OFFSET_STATUS:
+       case SNDRV_PCM_MMAP_OFFSET_STATUS_OLD:
+               if (pcm_file->no_compat_mmap || !IS_ENABLED(CONFIG_64BIT))
+                       return -ENXIO;
+               /* fallthrough */
+       case SNDRV_PCM_MMAP_OFFSET_STATUS_NEW:
                if (!pcm_status_mmap_allowed(pcm_file))
                        return -ENXIO;
                return snd_pcm_mmap_status(substream, file, area);
-       case SNDRV_PCM_MMAP_OFFSET_CONTROL:
+       case SNDRV_PCM_MMAP_OFFSET_CONTROL_OLD:
+               if (pcm_file->no_compat_mmap || !IS_ENABLED(CONFIG_64BIT))
+                       return -ENXIO;
+               /* fallthrough */
+       case SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW:
                if (!pcm_control_mmap_allowed(pcm_file))
                        return -ENXIO;
                return snd_pcm_mmap_control(substream, file, area);
@@ -3722,9 +3898,9 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file,
        unsigned long offset = pgoff << PAGE_SHIFT;
 
        switch (offset) {
-       case SNDRV_PCM_MMAP_OFFSET_STATUS:
+       case SNDRV_PCM_MMAP_OFFSET_STATUS_NEW:
                return (unsigned long)runtime->status;
-       case SNDRV_PCM_MMAP_OFFSET_CONTROL:
+       case SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW:
                return (unsigned long)runtime->control;
        default:
                return (unsigned long)runtime->dma_area + offset;