diff mbox

[RFC,2/7] sound: core: Avoid using timespec for struct snd_pcm_status

Message ID 78aa803db47d99c2bee1a4dc8d426621324785b8.1505973912.git.baolin.wang@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

(Exiting) Baolin Wang Sept. 21, 2017, 6:18 a.m. UTC
The struct snd_pcm_status will use 'timespec' type variables to record
timestamp, which is not year 2038 safe on 32bits system.

Userspace will use SNDRV_PCM_IOCTL_STATUS and SNDRV_PCM_IOCTL_STATUS_EXT
as commands to issue ioctl() to fill the 'snd_pcm_status' structure in
userspace. The command number is always defined through _IOR/_IOW/IORW,
so when userspace changes the definition of 'struct timespec' to use
64-bit types, the command number also changes.

Thus in the kernel, we now need to define two versions of each such ioctl
and corresponding ioctl commands to handle 32bit time_t and 64bit time_t
in native mode:
struct snd_pcm_status32 {
	......
	struct { s32 tv_sec; s32 tv_nsec; } trigger_tstamp;
	struct { s32 tv_sec; s32 tv_nsec; } tstamp;
	......
}

struct snd_pcm_status64 {
	......
	struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp;
	struct { s64 tv_sec; s64 tv_nsec; } tstamp;
	......
}

Moreover in compat file, we renamed or introduced new structures to handle
32bit/64bit time_t in compatible mode. 'struct compat_snd_pcm_status32' and
snd_pcm_status_user_compat() are used to handle 32bit time_t in compat mode.
'struct compat_snd_pcm_status64' and snd_pcm_status_user_compat64() are used
to handle 64bit time_t with 64bit alignment. 'struct compat_snd_pcm_status64_x86_32'
and snd_pcm_status_user_compat64_x86_32() are used to handle 64bit time_t with
32bit alignment.

Finally we can replace SNDRV_PCM_IOCTL_STATUS and SNDRV_PCM_IOCTL_STATUS_EXT
with new commands and introduce new functions to fill new 'struct snd_pcm_status64'
instead of using unsafe 'struct snd_pcm_status'. Then in future, the new
commands can be matched when userspace changes 'timespec' to 64bit type
to make a size change of 'struct snd_pcm_status'. When glibc changes time_t
to 64-bit, any recompiled program will issue ioctl commands that the kernel
does not understand without this patch.

Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
---
 include/sound/pcm.h     |   49 +++++++++-
 sound/core/pcm.c        |    8 +-
 sound/core/pcm_compat.c |  238 ++++++++++++++++++++++++++++++++++++-----------
 sound/core/pcm_native.c |  108 +++++++++++++++++----
 4 files changed, 324 insertions(+), 79 deletions(-)

Comments

Takashi Iwai Sept. 22, 2017, 9:31 a.m. UTC | #1
On Thu, 21 Sep 2017 08:18:04 +0200,
Baolin Wang wrote:
> 
> The struct snd_pcm_status will use 'timespec' type variables to record
> timestamp, which is not year 2038 safe on 32bits system.
> 
> Userspace will use SNDRV_PCM_IOCTL_STATUS and SNDRV_PCM_IOCTL_STATUS_EXT
> as commands to issue ioctl() to fill the 'snd_pcm_status' structure in
> userspace. The command number is always defined through _IOR/_IOW/IORW,
> so when userspace changes the definition of 'struct timespec' to use
> 64-bit types, the command number also changes.
> 
> Thus in the kernel, we now need to define two versions of each such ioctl
> and corresponding ioctl commands to handle 32bit time_t and 64bit time_t
> in native mode:
> struct snd_pcm_status32 {
> 	......
> 	struct { s32 tv_sec; s32 tv_nsec; } trigger_tstamp;
> 	struct { s32 tv_sec; s32 tv_nsec; } tstamp;
> 	......
> }
> 
> struct snd_pcm_status64 {
> 	......
> 	struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp;
> 	struct { s64 tv_sec; s64 tv_nsec; } tstamp;
> 	......
> }

I'm confused.  It's different from timespec64?  So 32bit user-space
would need to use a new own-type timespec instead of the standard
timespec that is compliant with y2038?


thanks,

Takashi
Arnd Bergmann Sept. 22, 2017, 10:14 a.m. UTC | #2
On Fri, Sep 22, 2017 at 11:31 AM, Takashi Iwai <tiwai@suse.de> wrote:
> On Thu, 21 Sep 2017 08:18:04 +0200,
> Baolin Wang wrote:
>>
>> The struct snd_pcm_status will use 'timespec' type variables to record
>> timestamp, which is not year 2038 safe on 32bits system.
>>
>> Userspace will use SNDRV_PCM_IOCTL_STATUS and SNDRV_PCM_IOCTL_STATUS_EXT
>> as commands to issue ioctl() to fill the 'snd_pcm_status' structure in
>> userspace. The command number is always defined through _IOR/_IOW/IORW,
>> so when userspace changes the definition of 'struct timespec' to use
>> 64-bit types, the command number also changes.
>>
>> Thus in the kernel, we now need to define two versions of each such ioctl
>> and corresponding ioctl commands to handle 32bit time_t and 64bit time_t
>> in native mode:
>> struct snd_pcm_status32 {
>>       ......
>>       struct { s32 tv_sec; s32 tv_nsec; } trigger_tstamp;
>>       struct { s32 tv_sec; s32 tv_nsec; } tstamp;
>>       ......
>> }
>>
>> struct snd_pcm_status64 {
>>       ......
>>       struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp;
>>       struct { s64 tv_sec; s64 tv_nsec; } tstamp;
>>       ......
>> }
>
> I'm confused.  It's different from timespec64?  So 32bit user-space
> would need to use a new own-type timespec instead of the standard
> timespec that is compliant with y2038?

It's complicated:

The definition of 'timespec' that user space sees comes from glibc,
and while that currently uses the traditional '{ long tv_sec;
long tv_nsec; }' definition, it will have to change to something like
(still simplified):

#if __32BIT && __64_BIT_TIME_T
typedef long long time_t;
#else
typedef long time_t;
#endif
struct timespec {
        time_t tv_sec;
#if __BIG_ENDIAN && __32BIT && __64_BIT_TIME_T
        unsigned int :32;
#endif
       long tv_nsec;
#if __LITTLE_ENDIAN && __32BIT && __64_BIT_TIME_T
        unsigned int pad;
#endif
} __attribute__((aligned(8)));

which matches the layout that a 64-bit kernel uses, aside
from the nanosecond padding.

The kernel uses timespec64 internally, which is defined as
"{ s64 tv_sec; long tv_nsec };", so this has the padding
in a different place on big-endian architectures, and has a
different alignment and size on i386. We plan to introduce
a 'struct __kernel_timespec' that is compatible with the
__64_BIT_TIME_T version of the user timespec, but that
doesn't exist yet.

If you prefer, we can probably introduce it now with Baolin's
series, I think Deepa was planning to post a patch to add
it soon anyway.

       Arnd
Takashi Iwai Sept. 22, 2017, 10:49 a.m. UTC | #3
On Fri, 22 Sep 2017 12:14:55 +0200,
Arnd Bergmann wrote:
> 
> On Fri, Sep 22, 2017 at 11:31 AM, Takashi Iwai <tiwai@suse.de> wrote:
> > On Thu, 21 Sep 2017 08:18:04 +0200,
> > Baolin Wang wrote:
> >>
> >> The struct snd_pcm_status will use 'timespec' type variables to record
> >> timestamp, which is not year 2038 safe on 32bits system.
> >>
> >> Userspace will use SNDRV_PCM_IOCTL_STATUS and SNDRV_PCM_IOCTL_STATUS_EXT
> >> as commands to issue ioctl() to fill the 'snd_pcm_status' structure in
> >> userspace. The command number is always defined through _IOR/_IOW/IORW,
> >> so when userspace changes the definition of 'struct timespec' to use
> >> 64-bit types, the command number also changes.
> >>
> >> Thus in the kernel, we now need to define two versions of each such ioctl
> >> and corresponding ioctl commands to handle 32bit time_t and 64bit time_t
> >> in native mode:
> >> struct snd_pcm_status32 {
> >>       ......
> >>       struct { s32 tv_sec; s32 tv_nsec; } trigger_tstamp;
> >>       struct { s32 tv_sec; s32 tv_nsec; } tstamp;
> >>       ......
> >> }
> >>
> >> struct snd_pcm_status64 {
> >>       ......
> >>       struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp;
> >>       struct { s64 tv_sec; s64 tv_nsec; } tstamp;
> >>       ......
> >> }
> >
> > I'm confused.  It's different from timespec64?  So 32bit user-space
> > would need to use a new own-type timespec instead of the standard
> > timespec that is compliant with y2038?
> 
> It's complicated:
> 
> The definition of 'timespec' that user space sees comes from glibc,
> and while that currently uses the traditional '{ long tv_sec;
> long tv_nsec; }' definition, it will have to change to something like
> (still simplified):
> 
> #if __32BIT && __64_BIT_TIME_T
> typedef long long time_t;
> #else
> typedef long time_t;
> #endif
> struct timespec {
>         time_t tv_sec;
> #if __BIG_ENDIAN && __32BIT && __64_BIT_TIME_T
>         unsigned int :32;
> #endif
>        long tv_nsec;
> #if __LITTLE_ENDIAN && __32BIT && __64_BIT_TIME_T
>         unsigned int pad;
> #endif
> } __attribute__((aligned(8)));
> 
> which matches the layout that a 64-bit kernel uses, aside
> from the nanosecond padding.

Wow, that is really messy.


> The kernel uses timespec64 internally, which is defined as
> "{ s64 tv_sec; long tv_nsec };", so this has the padding
> in a different place on big-endian architectures, and has a
> different alignment and size on i386. We plan to introduce
> a 'struct __kernel_timespec' that is compatible with the
> __64_BIT_TIME_T version of the user timespec, but that
> doesn't exist yet.
> 
> If you prefer, we can probably introduce it now with Baolin's
> series, I think Deepa was planning to post a patch to add
> it soon anyway.

Yes, this sounds like a saner solution than defining the own timespec
at each place individually.  Then we can have better conversion
macros, too, I suppose.

And, if we have kernel_timespec (or kernel_timespec64 or such), can
this deprecate the existing timespec64 usages, too?  I see that
timespec64 is internal only, so consolidation would be beneficial for
code simplification.


Thanks!

Takashi
Arnd Bergmann Sept. 22, 2017, 11:43 a.m. UTC | #4
On Fri, Sep 22, 2017 at 12:49 PM, Takashi Iwai <tiwai@suse.de> wrote:
> On Fri, 22 Sep 2017 12:14:55 +0200,
> Arnd Bergmann wrote:
>> The kernel uses timespec64 internally, which is defined as
>> "{ s64 tv_sec; long tv_nsec };", so this has the padding
>> in a different place on big-endian architectures, and has a
>> different alignment and size on i386. We plan to introduce
>> a 'struct __kernel_timespec' that is compatible with the
>> __64_BIT_TIME_T version of the user timespec, but that
>> doesn't exist yet.
>>
>> If you prefer, we can probably introduce it now with Baolin's
>> series, I think Deepa was planning to post a patch to add
>> it soon anyway.
>
> Yes, this sounds like a saner solution than defining the own timespec
> at each place individually.  Then we can have better conversion
> macros, too, I suppose.

Thinking about it again, we unfortunately can't use
__kernel_timespec until after all 32-bit architectures have
been converted to use the new syscalls that we still need
to introduce: In the meantime the plan is that '__kernel_timespec'
is an alias for the usual 'timespec' in user space and may still
be 32-bit wide.

I definitely agree that open-coding 'struct { s64 tv_sec;
s64 tv_nsec}' in a dozen locations is not overly helpful.

I suggested a different alternative in my reply to patch 3/7.
Can you have a look at that? The idea would be that we just
flatten all the structures in the ioctl implementation and make
the structure definition very explicit using u32/s32/u64/s64
members with no implied padding or architecture-specific
types.

> And, if we have kernel_timespec (or kernel_timespec64 or such), can
> this deprecate the existing timespec64 usages, too?  I see that
> timespec64 is internal only, so consolidation would be beneficial for
> code simplification.

Our current longterm plan is to only use __kernel_timespec on the
ABI side, where we have to watch out for the tricky conversion of
tv_nsec: Any timespec copied from a 32-bit process into the kernel
must ignore the upper half of the nanoseconds, while copying the
same structure from a 64-bit process must return an error if the
64-bit nanoseconds are larger than 999999999. When copying a
timespec into user space, we have to be careful to zero the upper
half of tv_nsec to avoid leaking uninitialized kernel data.

Inside of the kernel, we can ignore those constraints, so I'd keep
using the timespec64. We certainly don't want to use the 64-bit
nanoseconds field for internal uses on 32-bit kernels, as that
would introduce expensive 64-bit arithmetic in a lot of places
that don't need it.

My hope is also that we can eventually deprecate any use of the
plain 'timespec' in the kernel: all internal users should migrate
to timespec64 (one at a time, so we can properly review the
changes), and the uapi uses should either have the 64-bit
version of __kernel_timespec, or use compat_timespec once
that becomes usable on 32-bit architectures.

        Arnd
Takashi Iwai Sept. 22, 2017, 12:19 p.m. UTC | #5
On Fri, 22 Sep 2017 13:43:16 +0200,
Arnd Bergmann wrote:
> 
> On Fri, Sep 22, 2017 at 12:49 PM, Takashi Iwai <tiwai@suse.de> wrote:
> > On Fri, 22 Sep 2017 12:14:55 +0200,
> > Arnd Bergmann wrote:
> >> The kernel uses timespec64 internally, which is defined as
> >> "{ s64 tv_sec; long tv_nsec };", so this has the padding
> >> in a different place on big-endian architectures, and has a
> >> different alignment and size on i386. We plan to introduce
> >> a 'struct __kernel_timespec' that is compatible with the
> >> __64_BIT_TIME_T version of the user timespec, but that
> >> doesn't exist yet.
> >>
> >> If you prefer, we can probably introduce it now with Baolin's
> >> series, I think Deepa was planning to post a patch to add
> >> it soon anyway.
> >
> > Yes, this sounds like a saner solution than defining the own timespec
> > at each place individually.  Then we can have better conversion
> > macros, too, I suppose.
> 
> Thinking about it again, we unfortunately can't use
> __kernel_timespec until after all 32-bit architectures have
> been converted to use the new syscalls that we still need
> to introduce: In the meantime the plan is that '__kernel_timespec'
> is an alias for the usual 'timespec' in user space and may still
> be 32-bit wide.

OK.

> I definitely agree that open-coding 'struct { s64 tv_sec;
> s64 tv_nsec}' in a dozen locations is not overly helpful.
> 
> I suggested a different alternative in my reply to patch 3/7.
> Can you have a look at that? The idea would be that we just
> flatten all the structures in the ioctl implementation and make
> the structure definition very explicit using u32/s32/u64/s64
> members with no implied padding or architecture-specific
> types.

Thanks, I took a look at it, and I agree with you about the flatten
struct being easier to handle.


Though, one thing is still considered: snd_pcm_mmap_status struct is
not only passed via ioctl but it's embedded in a mmapped context.
So, differentiation by the struct size can't work with it (as already
you pointed about union, too).

It might sound like a contradiction what I wrote, but another possible
way would be to define 64bit timespec for the sound (both 64bit sec
and nsec), and use it consistently in all places no matter which
architecture is used.  It'd need the changes in alsa-lib or
tiny-alsa, of course.  But that's one of the reasons of the presence
of alsa-lib layer after all.

Recently we introduced the ioctl for user-space to inform the
supported ABI version (SNDRV_PCM_IOCTL_USER_PVERSION).  With this, we
can limit the 64bit timespec support only for the programs that issue
this ioctl with the new protocol version.  If not set or older ABI
version is given, it means still old 32bit timespec.  This can be used
for distinguish which mmapped struct is referred, too.

> > And, if we have kernel_timespec (or kernel_timespec64 or such), can
> > this deprecate the existing timespec64 usages, too?  I see that
> > timespec64 is internal only, so consolidation would be beneficial for
> > code simplification.
> 
> Our current longterm plan is to only use __kernel_timespec on the
> ABI side, where we have to watch out for the tricky conversion of
> tv_nsec: Any timespec copied from a 32-bit process into the kernel
> must ignore the upper half of the nanoseconds, while copying the
> same structure from a 64-bit process must return an error if the
> 64-bit nanoseconds are larger than 999999999. When copying a
> timespec into user space, we have to be careful to zero the upper
> half of tv_nsec to avoid leaking uninitialized kernel data.

Right.

> Inside of the kernel, we can ignore those constraints, so I'd keep
> using the timespec64. We certainly don't want to use the 64-bit
> nanoseconds field for internal uses on 32-bit kernels, as that
> would introduce expensive 64-bit arithmetic in a lot of places
> that don't need it.

OK, that's the reason of timespec64.  But this can be eventually
identical with __kernel_timespec?  It holds 64bit sec and 32bit nsec,
right?  The difference is the presence of padding, but having a pad
makes things arch-agnostic, so its cost looks paying well, I guess.

> My hope is also that we can eventually deprecate any use of the
> plain 'timespec' in the kernel: all internal users should migrate
> to timespec64 (one at a time, so we can properly review the
> changes), and the uapi uses should either have the 64-bit
> version of __kernel_timespec, or use compat_timespec once
> that becomes usable on 32-bit architectures.

Heh, let's keep our hope :)


thanks,

Takashi
diff mbox

Patch

diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index cd1ecd6..114cc29 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -58,6 +58,7 @@  struct snd_pcm_hardware {
 	size_t fifo_size;		/* fifo size in bytes */
 };
 
+struct snd_pcm_status64;
 struct snd_pcm_substream;
 
 struct snd_pcm_audio_tstamp_config; /* definitions further down */
@@ -565,8 +566,8 @@  struct snd_pcm_notify {
 int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info);
 int snd_pcm_info_user(struct snd_pcm_substream *substream,
 		      struct snd_pcm_info __user *info);
-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);
 int snd_pcm_start(struct snd_pcm_substream *substream);
 int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status);
 int snd_pcm_drain_done(struct snd_pcm_substream *substream);
@@ -1440,4 +1441,48 @@  static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format)
 #define pcm_dbg(pcm, fmt, args...) \
 	dev_dbg((pcm)->card->dev, fmt, ##args)
 
+struct snd_pcm_status64 {
+	snd_pcm_state_t state;		/* stream state */
+	struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp;	/* time when stream was started/stopped/paused */
+	struct { s64 tv_sec; s64 tv_nsec; } tstamp;		/* reference timestamp */
+	snd_pcm_uframes_t appl_ptr;	/* appl ptr */
+	snd_pcm_uframes_t hw_ptr;	/* hw ptr */
+	snd_pcm_sframes_t delay;	/* current delay in frames */
+	snd_pcm_uframes_t avail;	/* number of frames available */
+	snd_pcm_uframes_t avail_max;	/* max frames available on hw since last status */
+	snd_pcm_uframes_t overrange;	/* count of ADC (capture) overrange detections from last status */
+	snd_pcm_state_t suspended_state; /* suspended stream state */
+	__u32 audio_tstamp_data;	 /* needed for 64-bit alignment, used for configs/report to/from userspace */
+	struct { s64 tv_sec; s64 tv_nsec; } audio_tstamp;	/* sample counter, wall clock, PHC or on-demand sync'ed */
+	struct { s64 tv_sec; s64 tv_nsec; } driver_tstamp;	/* useful in case reference system tstamp is reported with delay */
+	__u32 audio_tstamp_accuracy;	/* in ns units, only valid if indicated in audio_tstamp_data */
+	unsigned char reserved[52-2*sizeof(struct { s64 tv_sec; s64 tv_nsec; })]; /* must be filled with zero */
+};
+
+#define SNDRV_PCM_IOCTL_STATUS64	_IOR('A', 0x20, struct snd_pcm_status64)
+#define SNDRV_PCM_IOCTL_STATUS_EXT64	_IOWR('A', 0x24, struct snd_pcm_status64)
+
+#if __BITS_PER_LONG == 32
+struct snd_pcm_status32 {
+	snd_pcm_state_t state;		/* stream state */
+	struct { s32 tv_sec; s32 tv_nsec; } trigger_tstamp;	/* time when stream was started/stopped/paused */
+	struct { s32 tv_sec; s32 tv_nsec; } tstamp;		/* reference timestamp */
+	snd_pcm_uframes_t appl_ptr;	/* appl ptr */
+	snd_pcm_uframes_t hw_ptr;	/* hw ptr */
+	snd_pcm_sframes_t delay;	/* current delay in frames */
+	snd_pcm_uframes_t avail;	/* number of frames available */
+	snd_pcm_uframes_t avail_max;	/* max frames available on hw since last status */
+	snd_pcm_uframes_t overrange;	/* count of ADC (capture) overrange detections from last status */
+	snd_pcm_state_t suspended_state; /* suspended stream state */
+	__u32 audio_tstamp_data;	 /* needed for 64-bit alignment, used for configs/report to/from userspace */
+	struct { s32 tv_sec; s32 tv_nsec; } audio_tstamp;	/* sample counter, wall clock, PHC or on-demand sync'ed */
+	struct { s32 tv_sec; s32 tv_nsec; } driver_tstamp;	/* useful in case reference system tstamp is reported with delay */
+	__u32 audio_tstamp_accuracy;	/* in ns units, only valid if indicated in audio_tstamp_data */
+	unsigned char reserved[52-2*sizeof(struct { s32 tv_sec; s32 tv_nsec; })]; /* must be filled with zero */
+};
+
+#define SNDRV_PCM_IOCTL_STATUS32	_IOR('A', 0x20, struct snd_pcm_status32)
+#define SNDRV_PCM_IOCTL_STATUS_EXT32	_IOWR('A', 0x24, struct snd_pcm_status32)
+#endif
+
 #endif /* __SOUND_PCM_H */
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 7eadb7f..2d990d9 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -453,7 +453,7 @@  static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
 {
 	struct snd_pcm_substream *substream = entry->private_data;
 	struct snd_pcm_runtime *runtime;
-	struct snd_pcm_status status;
+	struct snd_pcm_status64 status;
 	int err;
 
 	mutex_lock(&substream->pcm->open_mutex);
@@ -463,16 +463,16 @@  static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
 		goto unlock;
 	}
 	memset(&status, 0, sizeof(status));
-	err = snd_pcm_status(substream, &status);
+	err = snd_pcm_status64(substream, &status);
 	if (err < 0) {
 		snd_iprintf(buffer, "error %d\n", err);
 		goto unlock;
 	}
 	snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
 	snd_iprintf(buffer, "owner_pid   : %d\n", pid_vnr(substream->pid));
-	snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
+	snd_iprintf(buffer, "trigger_time: %lld.%09lld\n",
 		status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
-	snd_iprintf(buffer, "tstamp      : %ld.%09ld\n",
+	snd_iprintf(buffer, "tstamp      : %lld.%09lld\n",
 		status.tstamp.tv_sec, status.tstamp.tv_nsec);
 	snd_iprintf(buffer, "delay       : %ld\n", status.delay);
 	snd_iprintf(buffer, "avail       : %ld\n", status.avail);
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index b719d0b..79e7475 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -187,7 +187,7 @@  static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
 	snd_pcm_channel_info_user(s, p)
 #endif /* CONFIG_X86_X32 */
 
-struct snd_pcm_status32 {
+struct compat_snd_pcm_status32 {
 	s32 state;
 	struct compat_timespec trigger_tstamp;
 	struct compat_timespec tstamp;
@@ -207,13 +207,15 @@  struct snd_pcm_status32 {
 
 
 static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
-				      struct snd_pcm_status32 __user *src,
+				      struct compat_snd_pcm_status32 __user *src,
 				      bool ext)
 {
-	struct snd_pcm_status status;
+	struct snd_pcm_status64 status;
+	struct compat_snd_pcm_status32 compat_status32;
 	int err;
 
 	memset(&status, 0, sizeof(status));
+	memset(&compat_status32, 0, sizeof(compat_status32));
 	/*
 	 * with extension, parameters are read/write,
 	 * get audio_tstamp_data from user,
@@ -222,38 +224,53 @@  static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
 	if (ext && get_user(status.audio_tstamp_data,
 				(u32 __user *)(&src->audio_tstamp_data)))
 		return -EFAULT;
-	err = snd_pcm_status(substream, &status);
+	err = snd_pcm_status64(substream, &status);
 	if (err < 0)
 		return err;
 
 	if (clear_user(src, sizeof(*src)))
 		return -EFAULT;
-	if (put_user(status.state, &src->state) ||
-	    compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
-	    compat_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) ||
-	    compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
-	    compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
-	    put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
+
+	compat_status32 = (struct compat_snd_pcm_status32) {
+		.state = status.state,
+		.trigger_tstamp = {
+			.tv_sec = status.trigger_tstamp.tv_sec,
+			.tv_nsec = status.trigger_tstamp.tv_nsec,
+		},
+		.tstamp = {
+			.tv_sec = status.tstamp.tv_sec,
+			.tv_nsec = status.tstamp.tv_nsec,
+		},
+		.appl_ptr = status.appl_ptr,
+		.hw_ptr = status.hw_ptr,
+		.delay = status.delay,
+		.avail = status.avail,
+		.avail_max = status.avail_max,
+		.overrange = status.overrange,
+		.suspended_state = status.suspended_state,
+		.audio_tstamp_data = status.audio_tstamp_data,
+		.audio_tstamp = {
+			.tv_sec = status.audio_tstamp.tv_sec,
+			.tv_nsec = status.audio_tstamp.tv_nsec,
+		},
+		.driver_tstamp = {
+			.tv_sec = status.audio_tstamp.tv_sec,
+			.tv_nsec = status.audio_tstamp.tv_nsec,
+		},
+		.audio_tstamp_accuracy = status.audio_tstamp_accuracy,
+	};
+
+	if (copy_to_user(src, &compat_status32, sizeof(compat_status32)))
 		return -EFAULT;
 
 	return err;
 }
 
-#ifdef CONFIG_X86_X32
-/* X32 ABI has 64bit timespec and 64bit alignment */
-struct snd_pcm_status_x32 {
+struct compat_snd_pcm_status64 {
 	s32 state;
 	u32 rsvd; /* alignment */
-	struct timespec trigger_tstamp;
-	struct timespec tstamp;
+	struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp;
+	struct { s64 tv_sec; s64 tv_nsec; } tstamp;
 	u32 appl_ptr;
 	u32 hw_ptr;
 	s32 delay;
@@ -262,22 +279,24 @@  struct snd_pcm_status_x32 {
 	u32 overrange;
 	s32 suspended_state;
 	u32 audio_tstamp_data;
-	struct timespec audio_tstamp;
-	struct timespec driver_tstamp;
+	struct { s64 tv_sec; s64 tv_nsec; } audio_tstamp;
+	struct { s64 tv_sec; s64 tv_nsec; } driver_tstamp;
 	u32 audio_tstamp_accuracy;
-	unsigned char reserved[52-2*sizeof(struct timespec)];
+	unsigned char reserved[52-2*sizeof(struct { s64 tv_sec; s64 tv_nsec; })];
 } __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)
+static int snd_pcm_status_user_compat64(struct snd_pcm_substream *substream,
+					struct compat_snd_pcm_status64 __user *src,
+					bool ext)
 {
-	struct snd_pcm_status status;
+	struct snd_pcm_status64 status;
+	struct compat_snd_pcm_status64 compat_status64;
 	int err;
 
 	memset(&status, 0, sizeof(status));
+	memset(&compat_status64, 0, sizeof(compat_status64));
 	/*
 	 * with extension, parameters are read/write,
 	 * get audio_tstamp_data from user,
@@ -286,31 +305,128 @@  static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
 	if (ext && get_user(status.audio_tstamp_data,
 				(u32 __user *)(&src->audio_tstamp_data)))
 		return -EFAULT;
-	err = snd_pcm_status(substream, &status);
+	err = snd_pcm_status64(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))
+
+	compat_status64 = (struct compat_snd_pcm_status64) {
+		.state = status.state,
+		.trigger_tstamp = {
+			.tv_sec = status.trigger_tstamp.tv_sec,
+			.tv_nsec = status.trigger_tstamp.tv_nsec,
+		},
+		.tstamp = {
+			.tv_sec = status.tstamp.tv_sec,
+			.tv_nsec = status.tstamp.tv_nsec,
+		},
+		.appl_ptr = status.appl_ptr,
+		.hw_ptr = status.hw_ptr,
+		.delay = status.delay,
+		.avail = status.avail,
+		.avail_max = status.avail_max,
+		.overrange = status.overrange,
+		.suspended_state = status.suspended_state,
+		.audio_tstamp_data = status.audio_tstamp_data,
+		.audio_tstamp = {
+			.tv_sec = status.audio_tstamp.tv_sec,
+			.tv_nsec = status.audio_tstamp.tv_nsec,
+		},
+		.driver_tstamp = {
+			.tv_sec = status.audio_tstamp.tv_sec,
+			.tv_nsec = status.audio_tstamp.tv_nsec,
+		},
+		.audio_tstamp_accuracy = status.audio_tstamp_accuracy,
+	};
+
+	if (copy_to_user(src, &compat_status64, sizeof(compat_status64)))
 		return -EFAULT;
 
 	return err;
 }
-#endif /* CONFIG_X86_X32 */
+
+#ifdef IA32_EMULATION
+struct compat_snd_pcm_status64_x86_32 {
+	s32 state;
+	struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp;
+	struct { s64 tv_sec; s64 tv_nsec; } tstamp;
+	u32 appl_ptr;
+	u32 hw_ptr;
+	s32 delay;
+	u32 avail;
+	u32 avail_max;
+	u32 overrange;
+	s32 suspended_state;
+	u32 audio_tstamp_data;
+	struct { s64 tv_sec; s64 tv_nsec; } audio_tstamp;
+	struct { s64 tv_sec; s64 tv_nsec; } driver_tstamp;
+	u32 audio_tstamp_accuracy;
+	unsigned char reserved[52-2*sizeof(struct { s64 tv_sec; s64 tv_nsec; })];
+} __packed;
+
+static int
+snd_pcm_status_user_compat64_x86_32(struct snd_pcm_substream *substream,
+				    struct compat_snd_pcm_status64_x86_32 __user *src,
+				    bool ext)
+{
+	struct snd_pcm_status64 status;
+	struct compat_snd_pcm_status64_x86_32 status_x86_32;
+	int err;
+
+	memset(&status, 0, sizeof(status));
+	memset(&status_x86_32, 0, sizeof(status_x86_32));
+	/*
+	 * 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_status64(substream, &status);
+	if (err < 0)
+		return err;
+
+	if (clear_user(src, sizeof(*src)))
+		return -EFAULT;
+
+	status_x86_32 = (struct compat_snd_pcm_status64_x86_32) {
+		.state = status.state,
+		.trigger_tstamp = {
+			.tv_sec = status.trigger_tstamp.tv_sec,
+			.tv_nsec = status.trigger_tstamp.tv_nsec,
+		},
+		.tstamp = {
+			.tv_sec = status.tstamp.tv_sec,
+			.tv_nsec = status.tstamp.tv_nsec,
+		},
+		.appl_ptr = status.appl_ptr,
+		.hw_ptr = status.hw_ptr,
+		.delay = status.delay,
+		.avail = status.avail,
+		.avail_max = status.avail_max,
+		.overrange = status.overrange,
+		.suspended_state = status.suspended_state,
+		.audio_tstamp_data = status.audio_tstamp_data,
+		.audio_tstamp = {
+			.tv_sec = status.audio_tstamp.tv_sec,
+			.tv_nsec = status.audio_tstamp.tv_nsec,
+		},
+		.driver_tstamp = {
+			.tv_sec = status.audio_tstamp.tv_sec,
+			.tv_nsec = status.audio_tstamp.tv_nsec,
+		},
+		.audio_tstamp_accuracy = status.audio_tstamp_accuracy,
+	};
+
+	if (copy_to_user(src, &status_x86_32, sizeof(status_x86_32)))
+		return -EFAULT;
+
+	return err;
+}
+#endif
 
 /* both for HW_PARAMS and HW_REFINE */
 static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
@@ -633,8 +749,8 @@  enum {
 	SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32),
 	SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
 	SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
-	SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32),
-	SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32),
+	SNDRV_PCM_IOCTL_STATUS_COMPAT32 = _IOR('A', 0x20, struct compat_snd_pcm_status32),
+	SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32 = _IOWR('A', 0x24, struct compat_snd_pcm_status32),
 	SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
 	SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
 	SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
@@ -644,10 +760,14 @@  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),
+	SNDRV_PCM_IOCTL_STATUS_COMPAT64 = _IOR('A', 0x20, struct compat_snd_pcm_status64),
+	SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64 = _IOWR('A', 0x24, struct compat_snd_pcm_status64),
+#ifdef IA32_EMULATION
+	SNDRV_PCM_IOCTL_STATUS_COMPAT64_X86_32 = _IOR('A', 0x20, struct compat_snd_pcm_status64_x86_32),
+	SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64_X86_32 = _IOWR('A', 0x24, struct compat_snd_pcm_status64_x86_32),
+#endif
 #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 */
 };
@@ -697,9 +817,9 @@  static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
 		return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
 	case SNDRV_PCM_IOCTL_SW_PARAMS32:
 		return snd_pcm_ioctl_sw_params_compat(substream, argp);
-	case SNDRV_PCM_IOCTL_STATUS32:
+	case SNDRV_PCM_IOCTL_STATUS_COMPAT32:
 		return snd_pcm_status_user_compat(substream, argp, false);
-	case SNDRV_PCM_IOCTL_STATUS_EXT32:
+	case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32:
 		return snd_pcm_status_user_compat(substream, argp, true);
 	case SNDRV_PCM_IOCTL_SYNC_PTR32:
 		return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
@@ -719,11 +839,17 @@  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);
+	case SNDRV_PCM_IOCTL_STATUS_COMPAT64:
+		return snd_pcm_status_user_compat64(substream, argp, false);
+	case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64:
+		return snd_pcm_status_user_compat64(substream, argp, true);
+#ifdef IA32_EMULATION
+	case SNDRV_PCM_IOCTL_STATUS_COMPAT64_X86_32:
+		return snd_pcm_status_user_compat64_x86_32(substream, argp, false);
+	case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64_X86_32:
+		return snd_pcm_status_user_compat64_x86_32(substream, argp, true);
+#endif
 #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:
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 60bc303..7f1f60d 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -854,8 +854,8 @@  static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream,
 	return err;
 }
 
-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;
 
@@ -881,14 +881,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 = timespec64_to_timespec(runtime->trigger_tstamp);
+	status->trigger_tstamp.tv_sec = runtime->trigger_tstamp.tv_sec;
+	status->trigger_tstamp.tv_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 = timespec64_to_timespec(runtime->driver_tstamp);
-			status->audio_tstamp =
-				runtime->status->audio_tstamp;
+			status->tstamp.tv_sec = runtime->status->tstamp.tv_sec;
+			status->tstamp.tv_nsec =
+				runtime->status->tstamp.tv_nsec;
+			status->driver_tstamp.tv_sec =
+				runtime->driver_tstamp.tv_sec;
+			status->driver_tstamp.tv_nsec =
+				runtime->driver_tstamp.tv_nsec;
+			status->audio_tstamp.tv_sec =
+				runtime->status->audio_tstamp.tv_sec;
+			status->audio_tstamp.tv_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,
@@ -903,7 +911,8 @@  int snd_pcm_status(struct snd_pcm_substream *substream,
 			struct timespec64 tstamp;
 
 			snd_pcm_gettime(runtime, &tstamp);
-			status->tstamp = timespec64_to_timespec(tstamp);
+			status->tstamp.tv_sec = tstamp.tv_sec;
+			status->tstamp.tv_nsec = tstamp.tv_nsec;
 		}
 	}
  _tstamp_end:
@@ -933,11 +942,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));
@@ -949,7 +958,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)))
@@ -957,6 +966,65 @@  static int snd_pcm_status_user(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+#if __BITS_PER_LONG == 32
+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 = {
+			.tv_sec = status64.trigger_tstamp.tv_sec,
+			.tv_nsec = status64.trigger_tstamp.tv_nsec,
+		},
+		.tstamp = {
+			.tv_sec = status64.tstamp.tv_sec,
+			.tv_nsec = status64.tstamp.tv_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 = {
+			.tv_sec = status64.audio_tstamp.tv_sec,
+			.tv_nsec = status64.audio_tstamp.tv_nsec,
+		},
+		.driver_tstamp = {
+			.tv_sec = status64.audio_tstamp.tv_sec,
+			.tv_nsec = status64.audio_tstamp.tv_nsec,
+		},
+		.audio_tstamp_accuracy = status64.audio_tstamp_accuracy,
+	};
+
+	if (copy_to_user(_status, &status32, sizeof(status32)))
+		return -EFAULT;
+
+	return 0;
+}
+#endif
+
 static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
 				struct snd_pcm_channel_info * info)
 {
@@ -2888,10 +2956,16 @@  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);
+#if __BITS_PER_LONG == 32
+	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);
+#endif
+	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: