|
@@ -183,6 +183,14 @@ static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_X86_X32
|
|
|
+/* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */
|
|
|
+static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
|
|
|
+ struct snd_pcm_channel_info __user *src);
|
|
|
+#define snd_pcm_ioctl_channel_info_x32(s, p) \
|
|
|
+ snd_pcm_channel_info_user(s, p)
|
|
|
+#endif /* CONFIG_X86_X32 */
|
|
|
+
|
|
|
struct snd_pcm_status32 {
|
|
|
s32 state;
|
|
|
struct compat_timespec trigger_tstamp;
|
|
@@ -243,6 +251,71 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_X86_X32
|
|
|
+/* X32 ABI has 64bit timespec and 64bit alignment */
|
|
|
+struct snd_pcm_status_x32 {
|
|
|
+ s32 state;
|
|
|
+ u32 rsvd; /* alignment */
|
|
|
+ struct timespec trigger_tstamp;
|
|
|
+ struct timespec tstamp;
|
|
|
+ u32 appl_ptr;
|
|
|
+ u32 hw_ptr;
|
|
|
+ s32 delay;
|
|
|
+ u32 avail;
|
|
|
+ u32 avail_max;
|
|
|
+ u32 overrange;
|
|
|
+ s32 suspended_state;
|
|
|
+ u32 audio_tstamp_data;
|
|
|
+ struct timespec audio_tstamp;
|
|
|
+ struct timespec driver_tstamp;
|
|
|
+ u32 audio_tstamp_accuracy;
|
|
|
+ unsigned char reserved[52-2*sizeof(struct timespec)];
|
|
|
+} __packed;
|
|
|
+
|
|
|
+#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
|
|
|
+
|
|
|
+static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
|
|
|
+ struct snd_pcm_status_x32 __user *src,
|
|
|
+ bool ext)
|
|
|
+{
|
|
|
+ struct snd_pcm_status status;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ memset(&status, 0, sizeof(status));
|
|
|
+ /*
|
|
|
+ * with extension, parameters are read/write,
|
|
|
+ * get audio_tstamp_data from user,
|
|
|
+ * ignore rest of status structure
|
|
|
+ */
|
|
|
+ if (ext && get_user(status.audio_tstamp_data,
|
|
|
+ (u32 __user *)(&src->audio_tstamp_data)))
|
|
|
+ return -EFAULT;
|
|
|
+ err = snd_pcm_status(substream, &status);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (clear_user(src, sizeof(*src)))
|
|
|
+ return -EFAULT;
|
|
|
+ if (put_user(status.state, &src->state) ||
|
|
|
+ put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
|
|
|
+ put_timespec(&status.tstamp, &src->tstamp) ||
|
|
|
+ put_user(status.appl_ptr, &src->appl_ptr) ||
|
|
|
+ put_user(status.hw_ptr, &src->hw_ptr) ||
|
|
|
+ put_user(status.delay, &src->delay) ||
|
|
|
+ put_user(status.avail, &src->avail) ||
|
|
|
+ put_user(status.avail_max, &src->avail_max) ||
|
|
|
+ put_user(status.overrange, &src->overrange) ||
|
|
|
+ put_user(status.suspended_state, &src->suspended_state) ||
|
|
|
+ put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
|
|
|
+ put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
|
|
|
+ put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
|
|
|
+ put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+#endif /* CONFIG_X86_X32 */
|
|
|
+
|
|
|
/* both for HW_PARAMS and HW_REFINE */
|
|
|
static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
|
|
|
int refine,
|
|
@@ -469,6 +542,93 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_X86_X32
|
|
|
+/* X32 ABI has 64bit timespec and 64bit alignment */
|
|
|
+struct snd_pcm_mmap_status_x32 {
|
|
|
+ s32 state;
|
|
|
+ s32 pad1;
|
|
|
+ u32 hw_ptr;
|
|
|
+ u32 pad2; /* alignment */
|
|
|
+ struct timespec tstamp;
|
|
|
+ s32 suspended_state;
|
|
|
+ struct timespec audio_tstamp;
|
|
|
+} __packed;
|
|
|
+
|
|
|
+struct snd_pcm_mmap_control_x32 {
|
|
|
+ u32 appl_ptr;
|
|
|
+ u32 avail_min;
|
|
|
+};
|
|
|
+
|
|
|
+struct snd_pcm_sync_ptr_x32 {
|
|
|
+ u32 flags;
|
|
|
+ u32 rsvd; /* alignment */
|
|
|
+ union {
|
|
|
+ struct snd_pcm_mmap_status_x32 status;
|
|
|
+ unsigned char reserved[64];
|
|
|
+ } s;
|
|
|
+ union {
|
|
|
+ struct snd_pcm_mmap_control_x32 control;
|
|
|
+ unsigned char reserved[64];
|
|
|
+ } c;
|
|
|
+} __packed;
|
|
|
+
|
|
|
+static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
|
|
|
+ struct snd_pcm_sync_ptr_x32 __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_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
|
|
|
+ put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
|
|
|
+ put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) ||
|
|
|
+ put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
|
|
|
+ put_user(scontrol.avail_min, &src->c.control.avail_min))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif /* CONFIG_X86_X32 */
|
|
|
|
|
|
/*
|
|
|
*/
|
|
@@ -487,7 +647,12 @@ enum {
|
|
|
SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
|
|
|
SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
|
|
|
SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32),
|
|
|
-
|
|
|
+#ifdef CONFIG_X86_X32
|
|
|
+ SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info),
|
|
|
+ SNDRV_PCM_IOCTL_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32),
|
|
|
+ SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32),
|
|
|
+ SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
|
|
|
+#endif /* CONFIG_X86_X32 */
|
|
|
};
|
|
|
|
|
|
static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
|
|
@@ -559,6 +724,16 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
|
|
|
return snd_pcm_ioctl_rewind_compat(substream, argp);
|
|
|
case SNDRV_PCM_IOCTL_FORWARD32:
|
|
|
return snd_pcm_ioctl_forward_compat(substream, argp);
|
|
|
+#ifdef CONFIG_X86_X32
|
|
|
+ case SNDRV_PCM_IOCTL_STATUS_X32:
|
|
|
+ return snd_pcm_status_user_x32(substream, argp, false);
|
|
|
+ case SNDRV_PCM_IOCTL_STATUS_EXT_X32:
|
|
|
+ return snd_pcm_status_user_x32(substream, argp, true);
|
|
|
+ case SNDRV_PCM_IOCTL_SYNC_PTR_X32:
|
|
|
+ return snd_pcm_ioctl_sync_ptr_x32(substream, argp);
|
|
|
+ case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
|
|
|
+ return snd_pcm_ioctl_channel_info_x32(substream, argp);
|
|
|
+#endif /* CONFIG_X86_X32 */
|
|
|
}
|
|
|
|
|
|
return -ENOIOCTLCMD;
|