Commit df1d6ea0 authored by Takashi Iwai's avatar Takashi Iwai

Merge tag 'y2038-alsa-v8-signed' of...

Merge tag 'y2038-alsa-v8-signed' of git://git.kernel.org:/pub/scm/linux/kernel/git/arnd/playground into for-next

ALSA: Fix year 2038 issue for sound subsystem

This is a series I worked on with Baolin in 2017 and 2018, but we
never quite managed to finish up the last pieces. During the
ALSA developer meetup at ELC-E 2018 in Edinburgh, a decision was
made to go with this approach for keeping best compatibility
with existing source code, and then I failed to follow up by
resending the patches.

Now I have patches for all remaining time_t uses in the kernel,
so it's absolutely time to revisit them. I have done more
review of the patches myself and found a couple of minor issues
that I have fixed up, otherwise the series is still the same as
before.

Conceptually, the idea of these patches is:

- 64-bit applications should see no changes at all, neither
  compile-time nor run-time.

- 32-bit code compiled with a 64-bit time_t currently
  does not work with ALSA, and requires kernel changes and/or
  sound/asound.h changes

- Most 32-bit code using these interfaces will work correctly
  on a modified kernel, with or without the uapi header changes.

- 32-bit code using SNDRV_TIMER_IOCTL_TREAD requires the
  updated header file for 64-bit time_t support

- 32-bit i386 user space with 64-bit time_t is broken for
  SNDRV_PCM_IOCTL_STATUS, SNDRV_RAWMIDI_IOCTL_STATUS and
  SNDRV_PCM_IOCTL_SYNC_PTR because of i386 alignment. This is also
  addressed by the updated uapi header.

- PCM mmap is currently supported on native x86 kernels
  (both 32-bit and 64-bit) but not for compat mode. This series breaks
  the 32-bit native mmap support for 32-bit time_t, but instead allows
  it for 64-bit time_t on both native and compat kernels. This seems to
  be the best trade-off, as mmap support is optional already, and most
  32-bit code runs in compat mode anyway.

- I've tried to avoid breaking compilation of 32-bit code
  as much as possible. Anything that does break however is likely code
  that is already broken on 64-bit time_t and needs source changes to
  fix them.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/arnd/playground.git y2038-alsa-v8
[2] https://lore.kernel.org/lkml/CAK8P3a2Os66+iwQYf97qh05W2JP8rmWao8zmKoHiXqVHvyYAJA@mail.gmail.com/T/#m6519cb07cfda08adf1dedea6596bb98892b4d5dcSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>

Changes since v7: (Arnd):
 - Fix a typo found by Ben Hutchings

Changes since v6: (Arnd):
 - Add a patch to update the API versions
 - Hide a timespec reference in #ifndef __KERNEL__ to remove the
   last reference to time_t
 - Use a more readable way to do padding and describe it in the
   changelog
 - Rebase to linux-5.5-rc1, changing include/sound/soc-component.h
   and sound/drivers/aloop.c as needed.

Changes since v5 (Arnd):
 - Rebased to linux-5.4-rc4
 - Updated to completely remove timespec and time_t references from alsa
 - found and fixed a few bugs

Changes since v4 (Baolin):
 - Add patch 5 to change trigger_tstamp member of struct snd_pcm_runtime.
 - Add patch 8 to change internal timespec.
 - Add more explanation in commit message.
 - Use ktime_get_real_ts64() in patch 6.
 - Split common code out into a separate function in patch 6.
 - Fix tu->tread bug in patch 6 and remove #if __BITS_PER_LONG == 64 macro.

Changes since v3:
 - Move struct snd_pcm_status32 to pcm.h file.
 - Modify comments and commit message.
 - Add new patch2 ~ patch6.

Changes since v2:
 - Renamed all structures to make clear.
 - Remove CONFIG_X86_X32 macro and introduced new compat_snd_pcm_status64_x86_32.

Changes since v1:
 - Add one macro for struct snd_pcm_status_32 which only active in 32bits kernel.
 - Convert pcm_compat.c to use struct snd_pcm_status_64.
 - Convert pcm_native.c to use struct snd_pcm_status_64.
parents d8cac620 1cfaef96
...@@ -44,6 +44,7 @@ struct snd_pcm_hardware { ...@@ -44,6 +44,7 @@ struct snd_pcm_hardware {
size_t fifo_size; /* fifo size in bytes */ size_t fifo_size; /* fifo size in bytes */
}; };
struct snd_pcm_status64;
struct snd_pcm_substream; struct snd_pcm_substream;
struct snd_pcm_audio_tstamp_config; /* definitions further down */ struct snd_pcm_audio_tstamp_config; /* definitions further down */
...@@ -62,7 +63,7 @@ struct snd_pcm_ops { ...@@ -62,7 +63,7 @@ struct snd_pcm_ops {
int (*sync_stop)(struct snd_pcm_substream *substream); int (*sync_stop)(struct snd_pcm_substream *substream);
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
int (*get_time_info)(struct snd_pcm_substream *substream, int (*get_time_info)(struct snd_pcm_substream *substream,
struct timespec *system_ts, struct timespec *audio_ts, struct timespec64 *system_ts, struct timespec64 *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report); struct snd_pcm_audio_tstamp_report *audio_tstamp_report);
int (*fill_silence)(struct snd_pcm_substream *substream, int channel, int (*fill_silence)(struct snd_pcm_substream *substream, int channel,
...@@ -343,7 +344,7 @@ static inline void snd_pcm_pack_audio_tstamp_report(__u32 *data, __u32 *accuracy ...@@ -343,7 +344,7 @@ static inline void snd_pcm_pack_audio_tstamp_report(__u32 *data, __u32 *accuracy
struct snd_pcm_runtime { struct snd_pcm_runtime {
/* -- Status -- */ /* -- Status -- */
struct snd_pcm_substream *trigger_master; struct snd_pcm_substream *trigger_master;
struct timespec trigger_tstamp; /* trigger timestamp */ struct timespec64 trigger_tstamp; /* trigger timestamp */
bool trigger_tstamp_latched; /* trigger timestamp latched in low-level driver/hardware */ bool trigger_tstamp_latched; /* trigger timestamp latched in low-level driver/hardware */
int overrange; int overrange;
snd_pcm_uframes_t avail_max; snd_pcm_uframes_t avail_max;
...@@ -421,7 +422,7 @@ struct snd_pcm_runtime { ...@@ -421,7 +422,7 @@ struct snd_pcm_runtime {
/* -- audio timestamp config -- */ /* -- audio timestamp config -- */
struct snd_pcm_audio_tstamp_config audio_tstamp_config; struct snd_pcm_audio_tstamp_config audio_tstamp_config;
struct snd_pcm_audio_tstamp_report audio_tstamp_report; struct snd_pcm_audio_tstamp_report audio_tstamp_report;
struct timespec driver_tstamp; struct timespec64 driver_tstamp;
#if IS_ENABLED(CONFIG_SND_PCM_OSS) #if IS_ENABLED(CONFIG_SND_PCM_OSS)
/* -- OSS things -- */ /* -- OSS things -- */
...@@ -558,8 +559,8 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree); ...@@ -558,8 +559,8 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree);
int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info); int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info);
int snd_pcm_info_user(struct snd_pcm_substream *substream, int snd_pcm_info_user(struct snd_pcm_substream *substream,
struct snd_pcm_info __user *info); struct snd_pcm_info __user *info);
int snd_pcm_status(struct snd_pcm_substream *substream, int snd_pcm_status64(struct snd_pcm_substream *substream,
struct snd_pcm_status *status); struct snd_pcm_status64 *status);
int snd_pcm_start(struct snd_pcm_substream *substream); 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_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status);
int snd_pcm_drain_done(struct snd_pcm_substream *substream); int snd_pcm_drain_done(struct snd_pcm_substream *substream);
...@@ -1155,22 +1156,22 @@ static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substrea ...@@ -1155,22 +1156,22 @@ static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substrea
} }
/** /**
* snd_pcm_gettime - Fill the timespec depending on the timestamp mode * snd_pcm_gettime - Fill the timespec64 depending on the timestamp mode
* @runtime: PCM runtime instance * @runtime: PCM runtime instance
* @tv: timespec to fill * @tv: timespec64 to fill
*/ */
static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime, static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime,
struct timespec *tv) struct timespec64 *tv)
{ {
switch (runtime->tstamp_type) { switch (runtime->tstamp_type) {
case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC: case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC:
ktime_get_ts(tv); ktime_get_ts64(tv);
break; break;
case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW: case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
getrawmonotonic(tv); ktime_get_raw_ts64(tv);
break; break;
default: default:
getnstimeofday(tv); ktime_get_real_ts64(tv);
break; break;
} }
} }
...@@ -1422,4 +1423,55 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format) ...@@ -1422,4 +1423,55 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format)
#define pcm_dbg(pcm, fmt, args...) \ #define pcm_dbg(pcm, fmt, args...) \
dev_dbg((pcm)->card->dev, fmt, ##args) dev_dbg((pcm)->card->dev, fmt, ##args)
struct snd_pcm_status64 {
snd_pcm_state_t state; /* stream state */
u8 rsvd[4];
s64 trigger_tstamp_sec; /* time when stream was started/stopped/paused */
s64 trigger_tstamp_nsec;
s64 tstamp_sec; /* reference timestamp */
s64 tstamp_nsec;
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 */
s64 audio_tstamp_sec; /* sample counter, wall clock, PHC or on-demand sync'ed */
s64 audio_tstamp_nsec;
s64 driver_tstamp_sec; /* useful in case reference system tstamp is reported with delay */
s64 driver_tstamp_nsec;
__u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */
unsigned char reserved[52-4*sizeof(s64)]; /* 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)
struct snd_pcm_status32 {
s32 state; /* stream state */
s32 trigger_tstamp_sec; /* time when stream was started/stopped/paused */
s32 trigger_tstamp_nsec;
s32 tstamp_sec; /* reference timestamp */
s32 tstamp_nsec;
u32 appl_ptr; /* appl ptr */
u32 hw_ptr; /* hw ptr */
s32 delay; /* current delay in frames */
u32 avail; /* number of frames available */
u32 avail_max; /* max frames available on hw since last status */
u32 overrange; /* count of ADC (capture) overrange detections from last status */
s32 suspended_state; /* suspended stream state */
u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */
s32 audio_tstamp_sec; /* sample counter, wall clock, PHC or on-demand sync'ed */
s32 audio_tstamp_nsec;
s32 driver_tstamp_sec; /* useful in case reference system tstamp is reported with delay */
s32 driver_tstamp_nsec;
u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */
unsigned char reserved[52-4*sizeof(s32)]; /* 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 /* __SOUND_PCM_H */ #endif /* __SOUND_PCM_H */
...@@ -93,8 +93,8 @@ struct snd_soc_component_driver { ...@@ -93,8 +93,8 @@ struct snd_soc_component_driver {
snd_pcm_uframes_t (*pointer)(struct snd_soc_component *component, snd_pcm_uframes_t (*pointer)(struct snd_soc_component *component,
struct snd_pcm_substream *substream); struct snd_pcm_substream *substream);
int (*get_time_info)(struct snd_soc_component *component, int (*get_time_info)(struct snd_soc_component *component,
struct snd_pcm_substream *substream, struct timespec *system_ts, struct snd_pcm_substream *substream, struct timespec64 *system_ts,
struct timespec *audio_ts, struct timespec64 *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report); struct snd_pcm_audio_tstamp_report *audio_tstamp_report);
int (*copy_user)(struct snd_soc_component *component, int (*copy_user)(struct snd_soc_component *component,
......
...@@ -89,7 +89,7 @@ struct snd_timer_instance { ...@@ -89,7 +89,7 @@ struct snd_timer_instance {
unsigned long ticks, unsigned long resolution); unsigned long ticks, unsigned long resolution);
void (*ccallback) (struct snd_timer_instance * timeri, void (*ccallback) (struct snd_timer_instance * timeri,
int event, int event,
struct timespec * tstamp, struct timespec64 * tstamp,
unsigned long resolution); unsigned long resolution);
void (*disconnect)(struct snd_timer_instance *timeri); void (*disconnect)(struct snd_timer_instance *timeri);
void *callback_data; void *callback_data;
...@@ -113,7 +113,7 @@ struct snd_timer_instance { ...@@ -113,7 +113,7 @@ struct snd_timer_instance {
*/ */
int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, struct snd_timer **rtimer); int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, struct snd_timer **rtimer);
void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp); void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp);
int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer); int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer);
int snd_timer_global_free(struct snd_timer *timer); int snd_timer_global_free(struct snd_timer *timer);
int snd_timer_global_register(struct snd_timer *timer); int snd_timer_global_register(struct snd_timer *timer);
......
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
#include <time.h> #include <time.h>
#endif #endif
#include <asm/byteorder.h>
/* /*
* protocol version * protocol version
*/ */
...@@ -154,7 +156,7 @@ struct snd_hwdep_dsp_image { ...@@ -154,7 +156,7 @@ struct snd_hwdep_dsp_image {
* * * *
*****************************************************************************/ *****************************************************************************/
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 14) #define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 15)
typedef unsigned long snd_pcm_uframes_t; typedef unsigned long snd_pcm_uframes_t;
typedef signed long snd_pcm_sframes_t; typedef signed long snd_pcm_sframes_t;
...@@ -301,7 +303,9 @@ typedef int __bitwise snd_pcm_subformat_t; ...@@ -301,7 +303,9 @@ typedef int __bitwise snd_pcm_subformat_t;
#define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */
#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */
#if (__BITS_PER_LONG == 32 && defined(__USE_TIME_BITS64)) || defined __KERNEL__
#define __SND_STRUCT_TIME64
#endif
typedef int __bitwise snd_pcm_state_t; typedef int __bitwise snd_pcm_state_t;
#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */ #define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */
...@@ -317,8 +321,17 @@ typedef int __bitwise snd_pcm_state_t; ...@@ -317,8 +321,17 @@ typedef int __bitwise snd_pcm_state_t;
enum { enum {
SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000, SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000,
SNDRV_PCM_MMAP_OFFSET_STATUS = 0x80000000, SNDRV_PCM_MMAP_OFFSET_STATUS_OLD = 0x80000000,
SNDRV_PCM_MMAP_OFFSET_CONTROL = 0x81000000, SNDRV_PCM_MMAP_OFFSET_CONTROL_OLD = 0x81000000,
SNDRV_PCM_MMAP_OFFSET_STATUS_NEW = 0x82000000,
SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW = 0x83000000,
#ifdef __SND_STRUCT_TIME64
SNDRV_PCM_MMAP_OFFSET_STATUS = SNDRV_PCM_MMAP_OFFSET_STATUS_NEW,
SNDRV_PCM_MMAP_OFFSET_CONTROL = SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW,
#else
SNDRV_PCM_MMAP_OFFSET_STATUS = SNDRV_PCM_MMAP_OFFSET_STATUS_OLD,
SNDRV_PCM_MMAP_OFFSET_CONTROL = SNDRV_PCM_MMAP_OFFSET_CONTROL_OLD,
#endif
}; };
union snd_pcm_sync_id { union snd_pcm_sync_id {
...@@ -456,8 +469,13 @@ enum { ...@@ -456,8 +469,13 @@ enum {
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED
}; };
#ifndef __KERNEL__
/* explicit padding avoids incompatibility between i386 and x86-64 */
typedef struct { unsigned char pad[sizeof(time_t) - sizeof(int)] __time_pad;
struct snd_pcm_status { struct snd_pcm_status {
snd_pcm_state_t state; /* stream state */ snd_pcm_state_t state; /* stream state */
__time_pad pad1; /* align to timespec */
struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */ struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */
struct timespec tstamp; /* reference timestamp */ struct timespec tstamp; /* reference timestamp */
snd_pcm_uframes_t appl_ptr; /* appl ptr */ snd_pcm_uframes_t appl_ptr; /* appl ptr */
...@@ -473,17 +491,48 @@ struct snd_pcm_status { ...@@ -473,17 +491,48 @@ struct snd_pcm_status {
__u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */ __u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */
unsigned char reserved[52-2*sizeof(struct timespec)]; /* must be filled with zero */ unsigned char reserved[52-2*sizeof(struct timespec)]; /* must be filled with zero */
}; };
#endif
/*
* For mmap operations, we need the 64-bit layout, both for compat mode,
* and for y2038 compatibility. For 64-bit applications, the two definitions
* are identical, so we keep the traditional version.
*/
#ifdef __SND_STRUCT_TIME64
#define __snd_pcm_mmap_status64 snd_pcm_mmap_status
#define __snd_pcm_mmap_control64 snd_pcm_mmap_control
#define __snd_pcm_sync_ptr64 snd_pcm_sync_ptr
#ifdef __KERNEL__
#define __snd_timespec64 __kernel_timespec
#else
#define __snd_timespec64 timespec
#endif
struct __snd_timespec {
__s32 tv_sec;
__s32 tv_nsec;
};
#else
#define __snd_pcm_mmap_status snd_pcm_mmap_status
#define __snd_pcm_mmap_control snd_pcm_mmap_control
#define __snd_pcm_sync_ptr snd_pcm_sync_ptr
#define __snd_timespec timespec
struct __snd_timespec64 {
__s64 tv_sec;
__s64 tv_nsec;
};
#endif
struct snd_pcm_mmap_status { struct __snd_pcm_mmap_status {
snd_pcm_state_t state; /* RO: state - SNDRV_PCM_STATE_XXXX */ snd_pcm_state_t state; /* RO: state - SNDRV_PCM_STATE_XXXX */
int pad1; /* Needed for 64 bit alignment */ int pad1; /* Needed for 64 bit alignment */
snd_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */ snd_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */
struct timespec tstamp; /* Timestamp */ struct __snd_timespec tstamp; /* Timestamp */
snd_pcm_state_t suspended_state; /* RO: suspended stream state */ snd_pcm_state_t suspended_state; /* RO: suspended stream state */
struct timespec audio_tstamp; /* from sample counter or wall clock */ struct __snd_timespec audio_tstamp; /* from sample counter or wall clock */
}; };
struct snd_pcm_mmap_control { struct __snd_pcm_mmap_control {
snd_pcm_uframes_t appl_ptr; /* RW: appl ptr (0...boundary-1) */ snd_pcm_uframes_t appl_ptr; /* RW: appl ptr (0...boundary-1) */
snd_pcm_uframes_t avail_min; /* RW: min available frames for wakeup */ snd_pcm_uframes_t avail_min; /* RW: min available frames for wakeup */
}; };
...@@ -492,14 +541,59 @@ struct snd_pcm_mmap_control { ...@@ -492,14 +541,59 @@ struct snd_pcm_mmap_control {
#define SNDRV_PCM_SYNC_PTR_APPL (1<<1) /* get appl_ptr from driver (r/w op) */ #define SNDRV_PCM_SYNC_PTR_APPL (1<<1) /* get appl_ptr from driver (r/w op) */
#define SNDRV_PCM_SYNC_PTR_AVAIL_MIN (1<<2) /* get avail_min from driver */ #define SNDRV_PCM_SYNC_PTR_AVAIL_MIN (1<<2) /* get avail_min from driver */
struct snd_pcm_sync_ptr { struct __snd_pcm_sync_ptr {
unsigned int flags; unsigned int flags;
union { union {
struct snd_pcm_mmap_status status; struct __snd_pcm_mmap_status status;
unsigned char reserved[64]; unsigned char reserved[64];
} s; } s;
union { union {
struct snd_pcm_mmap_control control; struct __snd_pcm_mmap_control control;
unsigned char reserved[64];
} c;
};
#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : defined(__BIG_ENDIAN)
typedef char __pad_before_uframe[sizeof(__u64) - sizeof(snd_pcm_uframes_t)];
typedef char __pad_after_uframe[0];
#endif
#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN)
typedef char __pad_before_uframe[0];
typedef char __pad_after_uframe[sizeof(__u64) - sizeof(snd_pcm_uframes_t)];
#endif
struct __snd_pcm_mmap_status64 {
__s32 state; /* RO: state - SNDRV_PCM_STATE_XXXX */
__u32 pad1; /* Needed for 64 bit alignment */
__pad_before_uframe __pad1;
snd_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */
__pad_after_uframe __pad2;
struct __snd_timespec64 tstamp; /* Timestamp */
__s32 suspended_state; /* RO: suspended stream state */
__u32 pad3; /* Needed for 64 bit alignment */
struct __snd_timespec64 audio_tstamp; /* sample counter or wall clock */
};
struct __snd_pcm_mmap_control64 {
__pad_before_uframe __pad1;
snd_pcm_uframes_t appl_ptr; /* RW: appl ptr (0...boundary-1) */
__pad_before_uframe __pad2;
__pad_before_uframe __pad3;
snd_pcm_uframes_t avail_min; /* RW: min available frames for wakeup */
__pad_after_uframe __pad4;
};
struct __snd_pcm_sync_ptr64 {
__u32 flags;
__u32 pad1;
union {
struct __snd_pcm_mmap_status64 status;
unsigned char reserved[64];
} s;
union {
struct __snd_pcm_mmap_control64 control;
unsigned char reserved[64]; unsigned char reserved[64];
} c; } c;
}; };
...@@ -584,6 +678,8 @@ enum { ...@@ -584,6 +678,8 @@ enum {
#define SNDRV_PCM_IOCTL_STATUS _IOR('A', 0x20, struct snd_pcm_status) #define SNDRV_PCM_IOCTL_STATUS _IOR('A', 0x20, struct snd_pcm_status)
#define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t) #define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t)
#define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22) #define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22)
#define __SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct __snd_pcm_sync_ptr)
#define __SNDRV_PCM_IOCTL_SYNC_PTR64 _IOWR('A', 0x23, struct __snd_pcm_sync_ptr64)
#define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr) #define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr)
#define SNDRV_PCM_IOCTL_STATUS_EXT _IOWR('A', 0x24, struct snd_pcm_status) #define SNDRV_PCM_IOCTL_STATUS_EXT _IOWR('A', 0x24, struct snd_pcm_status)
#define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info) #define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info)
...@@ -614,7 +710,7 @@ enum { ...@@ -614,7 +710,7 @@ enum {
* Raw MIDI section - /dev/snd/midi?? * Raw MIDI section - /dev/snd/midi??
*/ */
#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) #define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1)
enum { enum {
SNDRV_RAWMIDI_STREAM_OUTPUT = 0, SNDRV_RAWMIDI_STREAM_OUTPUT = 0,
...@@ -648,13 +744,16 @@ struct snd_rawmidi_params { ...@@ -648,13 +744,16 @@ struct snd_rawmidi_params {
unsigned char reserved[16]; /* reserved for future use */ unsigned char reserved[16]; /* reserved for future use */
}; };
#ifndef __KERNEL__
struct snd_rawmidi_status { struct snd_rawmidi_status {
int stream; int stream;
__time_pad pad1;
struct timespec tstamp; /* Timestamp */ struct timespec tstamp; /* Timestamp */
size_t avail; /* available bytes */ size_t avail; /* available bytes */
size_t xruns; /* count of overruns since last status (in bytes) */ size_t xruns; /* count of overruns since last status (in bytes) */
unsigned char reserved[16]; /* reserved for future use */ unsigned char reserved[16]; /* reserved for future use */
}; };
#endif
#define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int) #define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int)
#define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info) #define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info)
...@@ -667,7 +766,7 @@ struct snd_rawmidi_status { ...@@ -667,7 +766,7 @@ struct snd_rawmidi_status {
* Timer section - /dev/snd/timer * Timer section - /dev/snd/timer
*/ */
#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 6) #define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 7)
enum { enum {
SNDRV_TIMER_CLASS_NONE = -1, SNDRV_TIMER_CLASS_NONE = -1,
...@@ -761,6 +860,7 @@ struct snd_timer_params { ...@@ -761,6 +860,7 @@ struct snd_timer_params {
unsigned char reserved[60]; /* reserved */ unsigned char reserved[60]; /* reserved */
}; };
#ifndef __KERNEL__
struct snd_timer_status { struct snd_timer_status {
struct timespec tstamp; /* Timestamp - last update */ struct timespec tstamp; /* Timestamp - last update */
unsigned int resolution; /* current period resolution in ns */ unsigned int resolution; /* current period resolution in ns */
...@@ -769,10 +869,11 @@ struct snd_timer_status { ...@@ -769,10 +869,11 @@ struct snd_timer_status {
unsigned int queue; /* used queue size */ unsigned int queue; /* used queue size */
unsigned char reserved[64]; /* reserved */ unsigned char reserved[64]; /* reserved */
}; };
#endif
#define SNDRV_TIMER_IOCTL_PVERSION _IOR('T', 0x00, int) #define SNDRV_TIMER_IOCTL_PVERSION _IOR('T', 0x00, int)
#define SNDRV_TIMER_IOCTL_NEXT_DEVICE _IOWR('T', 0x01, struct snd_timer_id) #define SNDRV_TIMER_IOCTL_NEXT_DEVICE _IOWR('T', 0x01, struct snd_timer_id)
#define SNDRV_TIMER_IOCTL_TREAD _IOW('T', 0x02, int) #define SNDRV_TIMER_IOCTL_TREAD_OLD _IOW('T', 0x02, int)
#define SNDRV_TIMER_IOCTL_GINFO _IOWR('T', 0x03, struct snd_timer_ginfo) #define SNDRV_TIMER_IOCTL_GINFO _IOWR('T', 0x03, struct snd_timer_ginfo)
#define SNDRV_TIMER_IOCTL_GPARAMS _IOW('T', 0x04, struct snd_timer_gparams) #define SNDRV_TIMER_IOCTL_GPARAMS _IOW('T', 0x04, struct snd_timer_gparams)
#define SNDRV_TIMER_IOCTL_GSTATUS _IOWR('T', 0x05, struct snd_timer_gstatus) #define SNDRV_TIMER_IOCTL_GSTATUS _IOWR('T', 0x05, struct snd_timer_gstatus)
...@@ -785,6 +886,15 @@ struct snd_timer_status { ...@@ -785,6 +886,15 @@ struct snd_timer_status {
#define SNDRV_TIMER_IOCTL_STOP _IO('T', 0xa1) #define SNDRV_TIMER_IOCTL_STOP _IO('T', 0xa1)
#define SNDRV_TIMER_IOCTL_CONTINUE _IO('T', 0xa2) #define SNDRV_TIMER_IOCTL_CONTINUE _IO('T', 0xa2)
#define SNDRV_TIMER_IOCTL_PAUSE _IO('T', 0xa3) #define SNDRV_TIMER_IOCTL_PAUSE _IO('T', 0xa3)
#define SNDRV_TIMER_IOCTL_TREAD64 _IOW('T', 0xa4, int)
#if __BITS_PER_LONG == 64
#define SNDRV_TIMER_IOCTL_TREAD SNDRV_TIMER_IOCTL_TREAD_OLD
#else
#define SNDRV_TIMER_IOCTL_TREAD ((sizeof(__kernel_long_t) >= sizeof(time_t)) ? \
SNDRV_TIMER_IOCTL_TREAD_OLD : \
SNDRV_TIMER_IOCTL_TREAD64)
#endif
struct snd_timer_read { struct snd_timer_read {
unsigned int resolution; unsigned int resolution;
...@@ -810,11 +920,15 @@ enum { ...@@ -810,11 +920,15 @@ enum {
SNDRV_TIMER_EVENT_MRESUME = SNDRV_TIMER_EVENT_RESUME + 10, SNDRV_TIMER_EVENT_MRESUME = SNDRV_TIMER_EVENT_RESUME + 10,
}; };
#ifndef __KERNEL__
struct snd_timer_tread { struct snd_timer_tread {
int event; int event;
__time_pad pad1;
struct timespec tstamp; struct timespec tstamp;
unsigned int val; unsigned int val;
__time_pad pad2;
}; };
#endif
/**************************************************************************** /****************************************************************************
* * * *
...@@ -955,8 +1069,7 @@ struct snd_ctl_elem_value { ...@@ -955,8 +1069,7 @@ struct snd_ctl_elem_value {
} bytes; } bytes;
struct snd_aes_iec958 iec958; struct snd_aes_iec958 iec958;
} value; /* RO */ } value; /* RO */
struct timespec tstamp; unsigned char reserved[128];
unsigned char reserved[128-sizeof(struct timespec)];
}; };
struct snd_ctl_tlv { struct snd_ctl_tlv {
......
...@@ -443,7 +443,7 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, ...@@ -443,7 +443,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_substream *substream = entry->private_data;
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
struct snd_pcm_status status; struct snd_pcm_status64 status;
int err; int err;
mutex_lock(&substream->pcm->open_mutex); mutex_lock(&substream->pcm->open_mutex);
...@@ -453,17 +453,17 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, ...@@ -453,17 +453,17 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
goto unlock; goto unlock;
} }
memset(&status, 0, sizeof(status)); memset(&status, 0, sizeof(status));
err = snd_pcm_status(substream, &status); err = snd_pcm_status64(substream, &status);
if (err < 0) { if (err < 0) {
snd_iprintf(buffer, "error %d\n", err); snd_iprintf(buffer, "error %d\n", err);
goto unlock; goto unlock;
} }
snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); 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, "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); status.trigger_tstamp_sec, status.trigger_tstamp_nsec);
snd_iprintf(buffer, "tstamp : %ld.%09ld\n", snd_iprintf(buffer, "tstamp : %lld.%09lld\n",
status.tstamp.tv_sec, status.tstamp.tv_nsec); status.tstamp_sec, status.tstamp_nsec);
snd_iprintf(buffer, "delay : %ld\n", status.delay); snd_iprintf(buffer, "delay : %ld\n", status.delay);
snd_iprintf(buffer, "avail : %ld\n", status.avail); snd_iprintf(buffer, "avail : %ld\n", status.avail);
snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max);
......
...@@ -83,19 +83,6 @@ struct snd_pcm_sw_params32 { ...@@ -83,19 +83,6 @@ struct snd_pcm_sw_params32 {
unsigned char reserved[56]; unsigned char reserved[56];
}; };
/* 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_sw_params_compat(struct snd_pcm_substream *substream, static int snd_pcm_ioctl_sw_params_compat(struct snd_pcm_substream *substream,
struct snd_pcm_sw_params32 __user *src) struct snd_pcm_sw_params32 __user *src)
{ {
...@@ -168,10 +155,13 @@ static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream, ...@@ -168,10 +155,13 @@ static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
snd_pcm_channel_info_user(s, p) snd_pcm_channel_info_user(s, p)
#endif /* CONFIG_X86_X32 */ #endif /* CONFIG_X86_X32 */
struct snd_pcm_status32 { struct compat_snd_pcm_status64 {
s32 state; s32 state;
struct compat_timespec trigger_tstamp; u8 rsvd[4]; /* alignment */
struct compat_timespec tstamp; s64 trigger_tstamp_sec;
s64 trigger_tstamp_nsec;
s64 tstamp_sec;
s64 tstamp_nsec;
u32 appl_ptr; u32 appl_ptr;
u32 hw_ptr; u32 hw_ptr;
s32 delay; s32 delay;
...@@ -180,85 +170,24 @@ struct snd_pcm_status32 { ...@@ -180,85 +170,24 @@ struct snd_pcm_status32 {
u32 overrange; u32 overrange;
s32 suspended_state; s32 suspended_state;
u32 audio_tstamp_data; u32 audio_tstamp_data;
struct compat_timespec audio_tstamp; s64 audio_tstamp_sec;
struct compat_timespec driver_tstamp; s64 audio_tstamp_nsec;
s64 driver_tstamp_sec;
s64 driver_tstamp_nsec;
u32 audio_tstamp_accuracy; u32 audio_tstamp_accuracy;
unsigned char reserved[52-2*sizeof(struct compat_timespec)]; unsigned char reserved[52-4*sizeof(s64)];
} __attribute__((packed));
static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
struct snd_pcm_status32 __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) ||
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))
return -EFAULT;
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; } __packed;
#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst)) static int snd_pcm_status_user_compat64(struct snd_pcm_substream *substream,
struct compat_snd_pcm_status64 __user *src,
static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream, bool ext)
struct snd_pcm_status_x32 __user *src,
bool ext)
{ {
struct snd_pcm_status status; struct snd_pcm_status64 status;
struct compat_snd_pcm_status64 compat_status64;
int err; int err;
memset(&status, 0, sizeof(status)); memset(&status, 0, sizeof(status));
memset(&compat_status64, 0, sizeof(compat_status64));
/* /*
* with extension, parameters are read/write, * with extension, parameters are read/write,
* get audio_tstamp_data from user, * get audio_tstamp_data from user,
...@@ -267,31 +196,39 @@ static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream, ...@@ -267,31 +196,39 @@ static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
if (ext && get_user(status.audio_tstamp_data, if (ext && get_user(status.audio_tstamp_data,
(u32 __user *)(&src->audio_tstamp_data))) (u32 __user *)(&src->audio_tstamp_data)))
return -EFAULT; return -EFAULT;
err = snd_pcm_status(substream, &status); err = snd_pcm_status64(substream, &status);
if (err < 0) if (err < 0)
return err; return err;
if (clear_user(src, sizeof(*src))) if (clear_user(src, sizeof(*src)))
return -EFAULT; return -EFAULT;
if (put_user(status.state, &src->state) ||
put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) || compat_status64 = (struct compat_snd_pcm_status64) {
put_timespec(&status.tstamp, &src->tstamp) || .state = status.state,
put_user(status.appl_ptr, &src->appl_ptr) || .trigger_tstamp_sec = status.trigger_tstamp_sec,
put_user(status.hw_ptr, &src->hw_ptr) || .trigger_tstamp_nsec = status.trigger_tstamp_nsec,
put_user(status.delay, &src->delay) || .tstamp_sec = status.tstamp_sec,
put_user(status.avail, &src->avail) || .tstamp_nsec = status.tstamp_nsec,
put_user(status.avail_max, &src->avail_max) || .appl_ptr = status.appl_ptr,
put_user(status.overrange, &src->overrange) || .hw_ptr = status.hw_ptr,
put_user(status.suspended_state, &src->suspended_state) || .delay = status.delay,
put_user(status.audio_tstamp_data, &src->audio_tstamp_data) || .avail = status.avail,
put_timespec(&status.audio_tstamp, &src->audio_tstamp) || .avail_max = status.avail_max,
put_timespec(&status.driver_tstamp, &src->driver_tstamp) || .overrange = status.overrange,
put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy)) .suspended_state = status.suspended_state,
.audio_tstamp_data = status.audio_tstamp_data,
.audio_tstamp_sec = status.audio_tstamp_sec,
.audio_tstamp_nsec = status.audio_tstamp_nsec,
.driver_tstamp_sec = status.audio_tstamp_sec,
.driver_tstamp_nsec = status.audio_tstamp_nsec,
.audio_tstamp_accuracy = status.audio_tstamp_accuracy,
};
if (copy_to_user(src, &compat_status64, sizeof(compat_status64)))
return -EFAULT; return -EFAULT;
return err; return err;
} }
#endif /* CONFIG_X86_X32 */
/* both for HW_PARAMS and HW_REFINE */ /* both for HW_PARAMS and HW_REFINE */
static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
...@@ -436,91 +373,6 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream, ...@@ -436,91 +373,6 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
return err; return err;
} }
struct snd_pcm_mmap_status32 {
s32 state;
s32 pad1;
u32 hw_ptr;
struct compat_timespec tstamp;
s32 suspended_state;
struct compat_timespec audio_tstamp;
} __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));
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) ||
compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
compat_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;
}
#ifdef CONFIG_X86_X32 #ifdef CONFIG_X86_X32
/* X32 ABI has 64bit timespec and 64bit alignment */ /* X32 ABI has 64bit timespec and 64bit alignment */
struct snd_pcm_mmap_status_x32 { struct snd_pcm_mmap_status_x32 {
...@@ -528,10 +380,12 @@ struct snd_pcm_mmap_status_x32 { ...@@ -528,10 +380,12 @@ struct snd_pcm_mmap_status_x32 {
s32 pad1; s32 pad1;
u32 hw_ptr; u32 hw_ptr;
u32 pad2; /* alignment */ u32 pad2; /* alignment */
struct timespec tstamp; s64 tstamp_sec;
s64 tstamp_nsec;
s32 suspended_state; s32 suspended_state;
s32 pad3; s32 pad3;
struct timespec audio_tstamp; s64 audio_tstamp_sec;
s64 audio_tstamp_nsec;
} __packed; } __packed;
struct snd_pcm_mmap_control_x32 { struct snd_pcm_mmap_control_x32 {
...@@ -599,9 +453,11 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, ...@@ -599,9 +453,11 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
snd_pcm_stream_unlock_irq(substream); snd_pcm_stream_unlock_irq(substream);
if (put_user(sstatus.state, &src->s.status.state) || if (put_user(sstatus.state, &src->s.status.state) ||
put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
put_timespec(&sstatus.tstamp, &src->s.status.tstamp) || 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.suspended_state, &src->s.status.suspended_state) ||
put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) || 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.appl_ptr, &src->c.control.appl_ptr) ||
put_user(scontrol.avail_min, &src->c.control.avail_min)) put_user(scontrol.avail_min, &src->c.control.avail_min))
return -EFAULT; return -EFAULT;
...@@ -616,8 +472,8 @@ enum { ...@@ -616,8 +472,8 @@ enum {
SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32), 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_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_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_COMPAT32 = _IOR('A', 0x20, struct snd_pcm_status32),
SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32), SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32 = _IOWR('A', 0x24, struct snd_pcm_status32),
SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32), SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32), SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
...@@ -626,11 +482,10 @@ enum { ...@@ -626,11 +482,10 @@ enum {
SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct snd_xferi32), SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct snd_xferi32),
SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32), 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_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 CONFIG_X86_X32 #ifdef CONFIG_X86_X32
SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info), 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), SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
#endif /* CONFIG_X86_X32 */ #endif /* CONFIG_X86_X32 */
}; };
...@@ -650,8 +505,8 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l ...@@ -650,8 +505,8 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
/* /*
* When PCM is used on 32bit mode, we need to disable * When PCM is used on 32bit mode, we need to disable
* mmap of PCM status/control records because of the size * mmap of the old PCM status/control records because
* incompatibility. * of the size incompatibility.
*/ */
pcm_file->no_compat_mmap = 1; pcm_file->no_compat_mmap = 1;
...@@ -673,6 +528,13 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l ...@@ -673,6 +528,13 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_PCM_IOCTL_XRUN: case SNDRV_PCM_IOCTL_XRUN:
case SNDRV_PCM_IOCTL_LINK: case SNDRV_PCM_IOCTL_LINK:
case SNDRV_PCM_IOCTL_UNLINK: case SNDRV_PCM_IOCTL_UNLINK:
case __SNDRV_PCM_IOCTL_SYNC_PTR32:
return snd_pcm_common_ioctl(file, substream, cmd, argp);
case __SNDRV_PCM_IOCTL_SYNC_PTR64:
#ifdef CONFIG_X86_X32
if (in_x32_syscall())
return snd_pcm_ioctl_sync_ptr_x32(substream, argp);
#endif /* CONFIG_X86_X32 */
return snd_pcm_common_ioctl(file, substream, cmd, argp); return snd_pcm_common_ioctl(file, substream, cmd, argp);
case SNDRV_PCM_IOCTL_HW_REFINE32: case SNDRV_PCM_IOCTL_HW_REFINE32:
return snd_pcm_ioctl_hw_params_compat(substream, 1, argp); return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
...@@ -680,12 +542,10 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l ...@@ -680,12 +542,10 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
return snd_pcm_ioctl_hw_params_compat(substream, 0, argp); return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
case SNDRV_PCM_IOCTL_SW_PARAMS32: case SNDRV_PCM_IOCTL_SW_PARAMS32:
return snd_pcm_ioctl_sw_params_compat(substream, argp); 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); return snd_pcm_status_user32(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); return snd_pcm_status_user32(substream, argp, true);
case SNDRV_PCM_IOCTL_SYNC_PTR32:
return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
case SNDRV_PCM_IOCTL_CHANNEL_INFO32: case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
return snd_pcm_ioctl_channel_info_compat(substream, argp); return snd_pcm_ioctl_channel_info_compat(substream, argp);
case SNDRV_PCM_IOCTL_WRITEI_FRAMES32: case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
...@@ -702,13 +562,11 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l ...@@ -702,13 +562,11 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
return snd_pcm_ioctl_rewind_compat(substream, argp); return snd_pcm_ioctl_rewind_compat(substream, argp);
case SNDRV_PCM_IOCTL_FORWARD32: case SNDRV_PCM_IOCTL_FORWARD32:
return snd_pcm_ioctl_forward_compat(substream, argp); 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 CONFIG_X86_X32 #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: case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
return snd_pcm_ioctl_channel_info_x32(substream, argp); return snd_pcm_ioctl_channel_info_x32(substream, argp);
#endif /* CONFIG_X86_X32 */ #endif /* CONFIG_X86_X32 */
......
...@@ -144,8 +144,13 @@ void __snd_pcm_xrun(struct snd_pcm_substream *substream) ...@@ -144,8 +144,13 @@ void __snd_pcm_xrun(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
trace_xrun(substream); trace_xrun(substream);
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); struct timespec64 tstamp;
snd_pcm_gettime(runtime, &tstamp);
runtime->status->tstamp.tv_sec = tstamp.tv_sec;
runtime->status->tstamp.tv_nsec = tstamp.tv_nsec;
}
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
char name[16]; char name[16];
...@@ -200,12 +205,12 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, ...@@ -200,12 +205,12 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
} }
static void update_audio_tstamp(struct snd_pcm_substream *substream, static void update_audio_tstamp(struct snd_pcm_substream *substream,
struct timespec *curr_tstamp, struct timespec64 *curr_tstamp,
struct timespec *audio_tstamp) struct timespec64 *audio_tstamp)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
u64 audio_frames, audio_nsecs; u64 audio_frames, audio_nsecs;
struct timespec driver_tstamp; struct timespec64 driver_tstamp;
if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE) if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE)
return; return;
...@@ -229,18 +234,23 @@ static void update_audio_tstamp(struct snd_pcm_substream *substream, ...@@ -229,18 +234,23 @@ static void update_audio_tstamp(struct snd_pcm_substream *substream,
} }
audio_nsecs = div_u64(audio_frames * 1000000000LL, audio_nsecs = div_u64(audio_frames * 1000000000LL,
runtime->rate); runtime->rate);
*audio_tstamp = ns_to_timespec(audio_nsecs); *audio_tstamp = ns_to_timespec64(audio_nsecs);
} }
if (!timespec_equal(&runtime->status->audio_tstamp, audio_tstamp)) {
runtime->status->audio_tstamp = *audio_tstamp; if (runtime->status->audio_tstamp.tv_sec != audio_tstamp->tv_sec ||
runtime->status->tstamp = *curr_tstamp; runtime->status->audio_tstamp.tv_nsec != audio_tstamp->tv_nsec) {
runtime->status->audio_tstamp.tv_sec = audio_tstamp->tv_sec;
runtime->status->audio_tstamp.tv_nsec = audio_tstamp->tv_nsec;
runtime->status->tstamp.tv_sec = curr_tstamp->tv_sec;
runtime->status->tstamp.tv_nsec = curr_tstamp->tv_nsec;
} }
/* /*
* re-take a driver timestamp to let apps detect if the reference tstamp * re-take a driver timestamp to let apps detect if the reference tstamp
* read by low-level hardware was provided with a delay * read by low-level hardware was provided with a delay
*/ */
snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp); snd_pcm_gettime(substream->runtime, &driver_tstamp);
runtime->driver_tstamp = driver_tstamp; runtime->driver_tstamp = driver_tstamp;
} }
...@@ -253,8 +263,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, ...@@ -253,8 +263,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_pcm_sframes_t hdelta, delta; snd_pcm_sframes_t hdelta, delta;
unsigned long jdelta; unsigned long jdelta;
unsigned long curr_jiffies; unsigned long curr_jiffies;
struct timespec curr_tstamp; struct timespec64 curr_tstamp;
struct timespec audio_tstamp; struct timespec64 audio_tstamp;
int crossed_boundary = 0; int crossed_boundary = 0;
old_hw_ptr = runtime->status->hw_ptr; old_hw_ptr = runtime->status->hw_ptr;
...@@ -277,9 +287,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, ...@@ -277,9 +287,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */ /* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */
if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT) if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); snd_pcm_gettime(runtime, &curr_tstamp);
} else } else
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); snd_pcm_gettime(runtime, &curr_tstamp);
} }
if (pos == SNDRV_PCM_POS_XRUN) { if (pos == SNDRV_PCM_POS_XRUN) {
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Copyright (c) by Jaroslav Kysela <perex@perex.cz> * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/ */
#include <linux/compat.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/file.h> #include <linux/file.h>
...@@ -891,8 +892,8 @@ snd_pcm_calc_delay(struct snd_pcm_substream *substream) ...@@ -891,8 +892,8 @@ snd_pcm_calc_delay(struct snd_pcm_substream *substream)
return delay + substream->runtime->delay; return delay + substream->runtime->delay;
} }
int snd_pcm_status(struct snd_pcm_substream *substream, int snd_pcm_status64(struct snd_pcm_substream *substream,
struct snd_pcm_status *status) struct snd_pcm_status64 *status)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
...@@ -918,14 +919,22 @@ int snd_pcm_status(struct snd_pcm_substream *substream, ...@@ -918,14 +919,22 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
status->suspended_state = runtime->status->suspended_state; status->suspended_state = runtime->status->suspended_state;
if (status->state == SNDRV_PCM_STATE_OPEN) if (status->state == SNDRV_PCM_STATE_OPEN)
goto _end; 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)) { if (snd_pcm_running(substream)) {
snd_pcm_update_hw_ptr(substream); snd_pcm_update_hw_ptr(substream);
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
status->tstamp = runtime->status->tstamp; status->tstamp_sec = runtime->status->tstamp.tv_sec;
status->driver_tstamp = runtime->driver_tstamp; status->tstamp_nsec =
status->audio_tstamp = runtime->status->tstamp.tv_nsec;
runtime->status->audio_tstamp; 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) if (runtime->audio_tstamp_report.valid == 1)
/* backwards compatibility, no report provided in COMPAT mode */ /* backwards compatibility, no report provided in COMPAT mode */
snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data, snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data,
...@@ -936,8 +945,13 @@ int snd_pcm_status(struct snd_pcm_substream *substream, ...@@ -936,8 +945,13 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
} }
} else { } else {
/* get tstamp only in fallback mode and only if enabled */ /* get tstamp only in fallback mode and only if enabled */
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
snd_pcm_gettime(runtime, &status->tstamp); struct timespec64 tstamp;
snd_pcm_gettime(runtime, &tstamp);
status->tstamp_sec = tstamp.tv_sec;
status->tstamp_nsec = tstamp.tv_nsec;
}
} }
_tstamp_end: _tstamp_end:
status->appl_ptr = runtime->control->appl_ptr; status->appl_ptr = runtime->control->appl_ptr;
...@@ -954,11 +968,11 @@ int snd_pcm_status(struct snd_pcm_substream *substream, ...@@ -954,11 +968,11 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
return 0; return 0;
} }
static int snd_pcm_status_user(struct snd_pcm_substream *substream, static int snd_pcm_status_user64(struct snd_pcm_substream *substream,
struct snd_pcm_status __user * _status, struct snd_pcm_status64 __user * _status,
bool ext) bool ext)
{ {
struct snd_pcm_status status; struct snd_pcm_status64 status;
int res; int res;
memset(&status, 0, sizeof(status)); memset(&status, 0, sizeof(status));
...@@ -970,7 +984,7 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream, ...@@ -970,7 +984,7 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream,
if (ext && get_user(status.audio_tstamp_data, if (ext && get_user(status.audio_tstamp_data,
(u32 __user *)(&_status->audio_tstamp_data))) (u32 __user *)(&_status->audio_tstamp_data)))
return -EFAULT; return -EFAULT;
res = snd_pcm_status(substream, &status); res = snd_pcm_status64(substream, &status);
if (res < 0) if (res < 0)
return res; return res;
if (copy_to_user(_status, &status, sizeof(status))) if (copy_to_user(_status, &status, sizeof(status)))
...@@ -978,6 +992,55 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream, ...@@ -978,6 +992,55 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream,
return 0; 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, static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
struct snd_pcm_channel_info * info) struct snd_pcm_channel_info * info)
{ {
...@@ -2826,6 +2889,107 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, ...@@ -2826,6 +2889,107 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
return 0; 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) static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
...@@ -2955,10 +3119,14 @@ static int snd_pcm_common_ioctl(struct file *file, ...@@ -2955,10 +3119,14 @@ static int snd_pcm_common_ioctl(struct file *file,
return snd_pcm_hw_free(substream); return snd_pcm_hw_free(substream);
case SNDRV_PCM_IOCTL_SW_PARAMS: case SNDRV_PCM_IOCTL_SW_PARAMS:
return snd_pcm_sw_params_user(substream, arg); return snd_pcm_sw_params_user(substream, arg);
case SNDRV_PCM_IOCTL_STATUS: case SNDRV_PCM_IOCTL_STATUS32:
return snd_pcm_status_user(substream, arg, false); return snd_pcm_status_user32(substream, arg, false);
case SNDRV_PCM_IOCTL_STATUS_EXT: case SNDRV_PCM_IOCTL_STATUS_EXT32:
return snd_pcm_status_user(substream, arg, true); 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: case SNDRV_PCM_IOCTL_CHANNEL_INFO:
return snd_pcm_channel_info_user(substream, arg); return snd_pcm_channel_info_user(substream, arg);
case SNDRV_PCM_IOCTL_PREPARE: case SNDRV_PCM_IOCTL_PREPARE:
...@@ -2990,7 +3158,9 @@ static int snd_pcm_common_ioctl(struct file *file, ...@@ -2990,7 +3158,9 @@ static int snd_pcm_common_ioctl(struct file *file,
return -EFAULT; return -EFAULT;
return 0; 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); return snd_pcm_sync_ptr(substream, arg);
#ifdef CONFIG_SND_SUPPORT_OLD_API #ifdef CONFIG_SND_SUPPORT_OLD_API
case SNDRV_PCM_IOCTL_HW_REFINE_OLD: case SNDRV_PCM_IOCTL_HW_REFINE_OLD:
...@@ -3328,8 +3498,6 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file ...@@ -3328,8 +3498,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) 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. /* See pcm_control_mmap_allowed() below.
* Since older alsa-lib requires both status and control mmaps to be * 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. * coupled, we have to disable the status mmap for old alsa-lib, too.
...@@ -3554,11 +3722,19 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) ...@@ -3554,11 +3722,19 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
offset = area->vm_pgoff << PAGE_SHIFT; offset = area->vm_pgoff << PAGE_SHIFT;
switch (offset) { 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)) if (!pcm_status_mmap_allowed(pcm_file))
return -ENXIO; return -ENXIO;
return snd_pcm_mmap_status(substream, file, area); 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)) if (!pcm_control_mmap_allowed(pcm_file))
return -ENXIO; return -ENXIO;
return snd_pcm_mmap_control(substream, file, area); return snd_pcm_mmap_control(substream, file, area);
...@@ -3718,9 +3894,9 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file, ...@@ -3718,9 +3894,9 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file,
unsigned long offset = pgoff << PAGE_SHIFT; unsigned long offset = pgoff << PAGE_SHIFT;
switch (offset) { switch (offset) {
case SNDRV_PCM_MMAP_OFFSET_STATUS: case SNDRV_PCM_MMAP_OFFSET_STATUS_NEW:
return (unsigned long)runtime->status; return (unsigned long)runtime->status;
case SNDRV_PCM_MMAP_OFFSET_CONTROL: case SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW:
return (unsigned long)runtime->control; return (unsigned long)runtime->control;
default: default:
return (unsigned long)runtime->dma_area + offset; return (unsigned long)runtime->dma_area + offset;
......
...@@ -50,6 +50,29 @@ static DEFINE_MUTEX(register_mutex); ...@@ -50,6 +50,29 @@ static DEFINE_MUTEX(register_mutex);
#define rmidi_dbg(rmidi, fmt, args...) \ #define rmidi_dbg(rmidi, fmt, args...) \
dev_dbg(&(rmidi)->dev, fmt, ##args) dev_dbg(&(rmidi)->dev, fmt, ##args)
struct snd_rawmidi_status32 {
s32 stream;
s32 tstamp_sec; /* Timestamp */
s32 tstamp_nsec;
u32 avail; /* available bytes */
u32 xruns; /* count of overruns since last status (in bytes) */
unsigned char reserved[16]; /* reserved for future use */
};
#define SNDRV_RAWMIDI_IOCTL_STATUS32 _IOWR('W', 0x20, struct snd_rawmidi_status32)
struct snd_rawmidi_status64 {
int stream;
u8 rsvd[4]; /* alignment */
s64 tstamp_sec; /* Timestamp */
s64 tstamp_nsec;
size_t avail; /* available bytes */
size_t xruns; /* count of overruns since last status (in bytes) */
unsigned char reserved[16]; /* reserved for future use */
};
#define SNDRV_RAWMIDI_IOCTL_STATUS64 _IOWR('W', 0x20, struct snd_rawmidi_status64)
static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device) static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device)
{ {
struct snd_rawmidi *rawmidi; struct snd_rawmidi *rawmidi;
...@@ -677,7 +700,7 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, ...@@ -677,7 +700,7 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
EXPORT_SYMBOL(snd_rawmidi_input_params); EXPORT_SYMBOL(snd_rawmidi_input_params);
static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream, static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream,
struct snd_rawmidi_status *status) struct snd_rawmidi_status64 *status)
{ {
struct snd_rawmidi_runtime *runtime = substream->runtime; struct snd_rawmidi_runtime *runtime = substream->runtime;
...@@ -690,7 +713,7 @@ static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream, ...@@ -690,7 +713,7 @@ static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream,
} }
static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream, static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream,
struct snd_rawmidi_status *status) struct snd_rawmidi_status64 *status)
{ {
struct snd_rawmidi_runtime *runtime = substream->runtime; struct snd_rawmidi_runtime *runtime = substream->runtime;
...@@ -704,6 +727,80 @@ static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream, ...@@ -704,6 +727,80 @@ static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream,
return 0; return 0;
} }
static int snd_rawmidi_ioctl_status32(struct snd_rawmidi_file *rfile,
struct snd_rawmidi_status32 __user *argp)
{
int err = 0;
struct snd_rawmidi_status32 __user *status = argp;
struct snd_rawmidi_status32 status32;
struct snd_rawmidi_status64 status64;
if (copy_from_user(&status32, argp,
sizeof(struct snd_rawmidi_status32)))
return -EFAULT;
switch (status32.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
if (rfile->output == NULL)
return -EINVAL;
err = snd_rawmidi_output_status(rfile->output, &status64);
break;
case SNDRV_RAWMIDI_STREAM_INPUT:
if (rfile->input == NULL)
return -EINVAL;
err = snd_rawmidi_input_status(rfile->input, &status64);
break;
default:
return -EINVAL;
}
if (err < 0)
return err;
status32 = (struct snd_rawmidi_status32) {
.stream = status64.stream,
.tstamp_sec = status64.tstamp_sec,
.tstamp_nsec = status64.tstamp_nsec,
.avail = status64.avail,
.xruns = status64.xruns,
};
if (copy_to_user(status, &status32, sizeof(*status)))
return -EFAULT;
return 0;
}
static int snd_rawmidi_ioctl_status64(struct snd_rawmidi_file *rfile,
struct snd_rawmidi_status64 __user *argp)
{
int err = 0;
struct snd_rawmidi_status64 status;
if (copy_from_user(&status, argp, sizeof(struct snd_rawmidi_status64)))
return -EFAULT;
switch (status.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
if (rfile->output == NULL)
return -EINVAL;
err = snd_rawmidi_output_status(rfile->output, &status);
break;
case SNDRV_RAWMIDI_STREAM_INPUT:
if (rfile->input == NULL)
return -EINVAL;
err = snd_rawmidi_input_status(rfile->input, &status);
break;
default:
return -EINVAL;
}
if (err < 0)
return err;
if (copy_to_user(argp, &status,
sizeof(struct snd_rawmidi_status64)))
return -EFAULT;
return 0;
}
static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{ {
struct snd_rawmidi_file *rfile; struct snd_rawmidi_file *rfile;
...@@ -750,33 +847,10 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long ...@@ -750,33 +847,10 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
return -EINVAL; return -EINVAL;
} }
} }
case SNDRV_RAWMIDI_IOCTL_STATUS: case SNDRV_RAWMIDI_IOCTL_STATUS32:
{ return snd_rawmidi_ioctl_status32(rfile, argp);
int err = 0; case SNDRV_RAWMIDI_IOCTL_STATUS64:
struct snd_rawmidi_status status; return snd_rawmidi_ioctl_status64(rfile, argp);
if (copy_from_user(&status, argp, sizeof(struct snd_rawmidi_status)))
return -EFAULT;
switch (status.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
if (rfile->output == NULL)
return -EINVAL;
err = snd_rawmidi_output_status(rfile->output, &status);
break;
case SNDRV_RAWMIDI_STREAM_INPUT:
if (rfile->input == NULL)
return -EINVAL;
err = snd_rawmidi_input_status(rfile->input, &status);
break;
default:
return -EINVAL;
}
if (err < 0)
return err;
if (copy_to_user(argp, &status, sizeof(struct snd_rawmidi_status)))
return -EFAULT;
return 0;
}
case SNDRV_RAWMIDI_IOCTL_DROP: case SNDRV_RAWMIDI_IOCTL_DROP:
{ {
int val; int val;
......
...@@ -41,19 +41,22 @@ static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile, ...@@ -41,19 +41,22 @@ static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
return -EINVAL; return -EINVAL;
} }
struct snd_rawmidi_status32 { struct compat_snd_rawmidi_status64 {
s32 stream; s32 stream;
struct compat_timespec tstamp; u8 rsvd[4]; /* alignment */
s64 tstamp_sec;
s64 tstamp_nsec;
u32 avail; u32 avail;
u32 xruns; u32 xruns;
unsigned char reserved[16]; unsigned char reserved[16];
} __attribute__((packed)); } __attribute__((packed));
static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile, static int snd_rawmidi_ioctl_status_compat64(struct snd_rawmidi_file *rfile,
struct snd_rawmidi_status32 __user *src) struct compat_snd_rawmidi_status64 __user *src)
{ {
int err; int err;
struct snd_rawmidi_status status; struct snd_rawmidi_status64 status;
struct compat_snd_rawmidi_status64 compat_status;
if (get_user(status.stream, &src->stream)) if (get_user(status.stream, &src->stream))
return -EFAULT; return -EFAULT;
...@@ -75,68 +78,24 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile, ...@@ -75,68 +78,24 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
if (err < 0) if (err < 0)
return err; return err;
if (compat_put_timespec(&status.tstamp, &src->tstamp) || compat_status = (struct compat_snd_rawmidi_status64) {
put_user(status.avail, &src->avail) || .stream = status.stream,
put_user(status.xruns, &src->xruns)) .tstamp_sec = status.tstamp_sec,
return -EFAULT; .tstamp_nsec = status.tstamp_nsec,
.avail = status.avail,
return 0; .xruns = status.xruns,
} };
#ifdef CONFIG_X86_X32
/* X32 ABI has 64bit timespec and 64bit alignment */
struct snd_rawmidi_status_x32 {
s32 stream;
u32 rsvd; /* alignment */
struct timespec tstamp;
u32 avail;
u32 xruns;
unsigned char reserved[16];
} __attribute__((packed));
#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
static int snd_rawmidi_ioctl_status_x32(struct snd_rawmidi_file *rfile,
struct snd_rawmidi_status_x32 __user *src)
{
int err;
struct snd_rawmidi_status status;
if (get_user(status.stream, &src->stream))
return -EFAULT;
switch (status.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
if (!rfile->output)
return -EINVAL;
err = snd_rawmidi_output_status(rfile->output, &status);
break;
case SNDRV_RAWMIDI_STREAM_INPUT:
if (!rfile->input)
return -EINVAL;
err = snd_rawmidi_input_status(rfile->input, &status);
break;
default:
return -EINVAL;
}
if (err < 0)
return err;
if (put_timespec(&status.tstamp, &src->tstamp) || if (copy_to_user(src, &compat_status, sizeof(*src)))
put_user(status.avail, &src->avail) ||
put_user(status.xruns, &src->xruns))
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
#endif /* CONFIG_X86_X32 */
enum { enum {
SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct snd_rawmidi_params32), SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct snd_rawmidi_params32),
SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct snd_rawmidi_status32), SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT32 = _IOWR('W', 0x20, struct snd_rawmidi_status32),
#ifdef CONFIG_X86_X32 SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT64 = _IOWR('W', 0x20, struct compat_snd_rawmidi_status64),
SNDRV_RAWMIDI_IOCTL_STATUS_X32 = _IOWR('W', 0x20, struct snd_rawmidi_status_x32),
#endif /* CONFIG_X86_X32 */
}; };
static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
...@@ -153,12 +112,10 @@ static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsign ...@@ -153,12 +112,10 @@ static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsign
return snd_rawmidi_ioctl(file, cmd, (unsigned long)argp); return snd_rawmidi_ioctl(file, cmd, (unsigned long)argp);
case SNDRV_RAWMIDI_IOCTL_PARAMS32: case SNDRV_RAWMIDI_IOCTL_PARAMS32:
return snd_rawmidi_ioctl_params_compat(rfile, argp); return snd_rawmidi_ioctl_params_compat(rfile, argp);
case SNDRV_RAWMIDI_IOCTL_STATUS32: case SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT32:
return snd_rawmidi_ioctl_status_compat(rfile, argp); return snd_rawmidi_ioctl_status32(rfile, argp);
#ifdef CONFIG_X86_X32 case SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT64:
case SNDRV_RAWMIDI_IOCTL_STATUS_X32: return snd_rawmidi_ioctl_status_compat64(rfile, argp);
return snd_rawmidi_ioctl_status_x32(rfile, argp);
#endif /* CONFIG_X86_X32 */
} }
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
...@@ -44,6 +44,28 @@ MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for t ...@@ -44,6 +44,28 @@ MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for t
MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER); MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER);
MODULE_ALIAS("devname:snd/timer"); MODULE_ALIAS("devname:snd/timer");
enum timer_tread_format {
TREAD_FORMAT_NONE = 0,
TREAD_FORMAT_TIME64,
TREAD_FORMAT_TIME32,
};
struct snd_timer_tread32 {
int event;
s32 tstamp_sec;
s32 tstamp_nsec;
unsigned int val;
};
struct snd_timer_tread64 {
int event;
u8 pad1[4];
s64 tstamp_sec;
s64 tstamp_nsec;
unsigned int val;
u8 pad2[4];
};
struct snd_timer_user { struct snd_timer_user {
struct snd_timer_instance *timeri; struct snd_timer_instance *timeri;
int tread; /* enhanced read with timestamps and events */ int tread; /* enhanced read with timestamps and events */
...@@ -55,16 +77,40 @@ struct snd_timer_user { ...@@ -55,16 +77,40 @@ struct snd_timer_user {
int queue_size; int queue_size;
bool disconnected; bool disconnected;
struct snd_timer_read *queue; struct snd_timer_read *queue;
struct snd_timer_tread *tqueue; struct snd_timer_tread64 *tqueue;
spinlock_t qlock; spinlock_t qlock;
unsigned long last_resolution; unsigned long last_resolution;
unsigned int filter; unsigned int filter;
struct timespec tstamp; /* trigger tstamp */ struct timespec64 tstamp; /* trigger tstamp */
wait_queue_head_t qchange_sleep; wait_queue_head_t qchange_sleep;
struct fasync_struct *fasync; struct fasync_struct *fasync;
struct mutex ioctl_lock; struct mutex ioctl_lock;
}; };
struct snd_timer_status32 {
s32 tstamp_sec; /* Timestamp - last update */
s32 tstamp_nsec;
unsigned int resolution; /* current period resolution in ns */
unsigned int lost; /* counter of master tick lost */
unsigned int overrun; /* count of read queue overruns */
unsigned int queue; /* used queue size */
unsigned char reserved[64]; /* reserved */
};
#define SNDRV_TIMER_IOCTL_STATUS32 _IOR('T', 0x14, struct snd_timer_status32)
struct snd_timer_status64 {
s64 tstamp_sec; /* Timestamp - last update */
s64 tstamp_nsec;
unsigned int resolution; /* current period resolution in ns */
unsigned int lost; /* counter of master tick lost */
unsigned int overrun; /* count of read queue overruns */
unsigned int queue; /* used queue size */
unsigned char reserved[64]; /* reserved */
};
#define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64)
/* list of timers */ /* list of timers */
static LIST_HEAD(snd_timer_list); static LIST_HEAD(snd_timer_list);
...@@ -453,12 +499,12 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) ...@@ -453,12 +499,12 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
struct snd_timer *timer = ti->timer; struct snd_timer *timer = ti->timer;
unsigned long resolution = 0; unsigned long resolution = 0;
struct snd_timer_instance *ts; struct snd_timer_instance *ts;
struct timespec tstamp; struct timespec64 tstamp;
if (timer_tstamp_monotonic) if (timer_tstamp_monotonic)
ktime_get_ts(&tstamp); ktime_get_ts64(&tstamp);
else else
getnstimeofday(&tstamp); ktime_get_real_ts64(&tstamp);
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
event > SNDRV_TIMER_EVENT_PAUSE)) event > SNDRV_TIMER_EVENT_PAUSE))
return; return;
...@@ -1025,7 +1071,7 @@ static int snd_timer_dev_disconnect(struct snd_device *device) ...@@ -1025,7 +1071,7 @@ static int snd_timer_dev_disconnect(struct snd_device *device)
return 0; return 0;
} }
void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp) void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp)
{ {
unsigned long flags; unsigned long flags;
unsigned long resolution = 0; unsigned long resolution = 0;
...@@ -1305,7 +1351,7 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri, ...@@ -1305,7 +1351,7 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
} }
static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu, static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
struct snd_timer_tread *tread) struct snd_timer_tread64 *tread)
{ {
if (tu->qused >= tu->queue_size) { if (tu->qused >= tu->queue_size) {
tu->overrun++; tu->overrun++;
...@@ -1318,11 +1364,11 @@ static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu, ...@@ -1318,11 +1364,11 @@ static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
int event, int event,
struct timespec *tstamp, struct timespec64 *tstamp,
unsigned long resolution) unsigned long resolution)
{ {
struct snd_timer_user *tu = timeri->callback_data; struct snd_timer_user *tu = timeri->callback_data;
struct snd_timer_tread r1; struct snd_timer_tread64 r1;
unsigned long flags; unsigned long flags;
if (event >= SNDRV_TIMER_EVENT_START && if (event >= SNDRV_TIMER_EVENT_START &&
...@@ -1332,7 +1378,8 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, ...@@ -1332,7 +1378,8 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
return; return;
memset(&r1, 0, sizeof(r1)); memset(&r1, 0, sizeof(r1));
r1.event = event; r1.event = event;
r1.tstamp = *tstamp; r1.tstamp_sec = tstamp->tv_sec;
r1.tstamp_nsec = tstamp->tv_nsec;
r1.val = resolution; r1.val = resolution;
spin_lock_irqsave(&tu->qlock, flags); spin_lock_irqsave(&tu->qlock, flags);
snd_timer_user_append_to_tqueue(tu, &r1); snd_timer_user_append_to_tqueue(tu, &r1);
...@@ -1354,8 +1401,8 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, ...@@ -1354,8 +1401,8 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
unsigned long ticks) unsigned long ticks)
{ {
struct snd_timer_user *tu = timeri->callback_data; struct snd_timer_user *tu = timeri->callback_data;
struct snd_timer_tread *r, r1; struct snd_timer_tread64 *r, r1;
struct timespec tstamp; struct timespec64 tstamp;
int prev, append = 0; int prev, append = 0;
memset(&r1, 0, sizeof(r1)); memset(&r1, 0, sizeof(r1));
...@@ -1368,14 +1415,15 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, ...@@ -1368,14 +1415,15 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
} }
if (tu->last_resolution != resolution || ticks > 0) { if (tu->last_resolution != resolution || ticks > 0) {
if (timer_tstamp_monotonic) if (timer_tstamp_monotonic)
ktime_get_ts(&tstamp); ktime_get_ts64(&tstamp);
else else
getnstimeofday(&tstamp); ktime_get_real_ts64(&tstamp);
} }
if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
tu->last_resolution != resolution) { tu->last_resolution != resolution) {
r1.event = SNDRV_TIMER_EVENT_RESOLUTION; r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
r1.tstamp = tstamp; r1.tstamp_sec = tstamp.tv_sec;
r1.tstamp_nsec = tstamp.tv_nsec;
r1.val = resolution; r1.val = resolution;
snd_timer_user_append_to_tqueue(tu, &r1); snd_timer_user_append_to_tqueue(tu, &r1);
tu->last_resolution = resolution; tu->last_resolution = resolution;
...@@ -1389,14 +1437,16 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, ...@@ -1389,14 +1437,16 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
r = &tu->tqueue[prev]; r = &tu->tqueue[prev];
if (r->event == SNDRV_TIMER_EVENT_TICK) { if (r->event == SNDRV_TIMER_EVENT_TICK) {
r->tstamp = tstamp; r->tstamp_sec = tstamp.tv_sec;
r->tstamp_nsec = tstamp.tv_nsec;
r->val += ticks; r->val += ticks;
append++; append++;
goto __wake; goto __wake;
} }
} }
r1.event = SNDRV_TIMER_EVENT_TICK; r1.event = SNDRV_TIMER_EVENT_TICK;
r1.tstamp = tstamp; r1.tstamp_sec = tstamp.tv_sec;
r1.tstamp_nsec = tstamp.tv_nsec;
r1.val = ticks; r1.val = ticks;
snd_timer_user_append_to_tqueue(tu, &r1); snd_timer_user_append_to_tqueue(tu, &r1);
append++; append++;
...@@ -1411,7 +1461,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, ...@@ -1411,7 +1461,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
static int realloc_user_queue(struct snd_timer_user *tu, int size) static int realloc_user_queue(struct snd_timer_user *tu, int size)
{ {
struct snd_timer_read *queue = NULL; struct snd_timer_read *queue = NULL;
struct snd_timer_tread *tqueue = NULL; struct snd_timer_tread64 *tqueue = NULL;
if (tu->tread) { if (tu->tread) {
tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL); tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL);
...@@ -1850,11 +1900,11 @@ static int snd_timer_user_params(struct file *file, ...@@ -1850,11 +1900,11 @@ static int snd_timer_user_params(struct file *file,
tu->qhead = tu->qtail = tu->qused = 0; tu->qhead = tu->qtail = tu->qused = 0;
if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
if (tu->tread) { if (tu->tread) {
struct snd_timer_tread tread; struct snd_timer_tread64 tread;
memset(&tread, 0, sizeof(tread)); memset(&tread, 0, sizeof(tread));
tread.event = SNDRV_TIMER_EVENT_EARLY; tread.event = SNDRV_TIMER_EVENT_EARLY;
tread.tstamp.tv_sec = 0; tread.tstamp_sec = 0;
tread.tstamp.tv_nsec = 0; tread.tstamp_nsec = 0;
tread.val = 0; tread.val = 0;
snd_timer_user_append_to_tqueue(tu, &tread); snd_timer_user_append_to_tqueue(tu, &tread);
} else { } else {
...@@ -1875,17 +1925,41 @@ static int snd_timer_user_params(struct file *file, ...@@ -1875,17 +1925,41 @@ static int snd_timer_user_params(struct file *file,
return err; return err;
} }
static int snd_timer_user_status(struct file *file, static int snd_timer_user_status32(struct file *file,
struct snd_timer_status __user *_status) struct snd_timer_status32 __user *_status)
{
struct snd_timer_user *tu;
struct snd_timer_status32 status;
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
memset(&status, 0, sizeof(status));
status.tstamp_sec = tu->tstamp.tv_sec;
status.tstamp_nsec = tu->tstamp.tv_nsec;
status.resolution = snd_timer_resolution(tu->timeri);
status.lost = tu->timeri->lost;
status.overrun = tu->overrun;
spin_lock_irq(&tu->qlock);
status.queue = tu->qused;
spin_unlock_irq(&tu->qlock);
if (copy_to_user(_status, &status, sizeof(status)))
return -EFAULT;
return 0;
}
static int snd_timer_user_status64(struct file *file,
struct snd_timer_status64 __user *_status)
{ {
struct snd_timer_user *tu; struct snd_timer_user *tu;
struct snd_timer_status status; struct snd_timer_status64 status;
tu = file->private_data; tu = file->private_data;
if (!tu->timeri) if (!tu->timeri)
return -EBADFD; return -EBADFD;
memset(&status, 0, sizeof(status)); memset(&status, 0, sizeof(status));
status.tstamp = tu->tstamp; status.tstamp_sec = tu->tstamp.tv_sec;
status.tstamp_nsec = tu->tstamp.tv_nsec;
status.resolution = snd_timer_resolution(tu->timeri); status.resolution = snd_timer_resolution(tu->timeri);
status.lost = tu->timeri->lost; status.lost = tu->timeri->lost;
status.overrun = tu->overrun; status.overrun = tu->overrun;
...@@ -1960,6 +2034,36 @@ static int snd_timer_user_pause(struct file *file) ...@@ -1960,6 +2034,36 @@ static int snd_timer_user_pause(struct file *file)
return 0; return 0;
} }
static int snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu,
unsigned int cmd, bool compat)
{
int __user *p = argp;
int xarg, old_tread;
if (tu->timeri) /* too late */
return -EBUSY;
if (get_user(xarg, p))
return -EFAULT;
old_tread = tu->tread;
if (!xarg)
tu->tread = TREAD_FORMAT_NONE;
else if (cmd == SNDRV_TIMER_IOCTL_TREAD64 ||
(IS_ENABLED(CONFIG_64BIT) && !compat))
tu->tread = TREAD_FORMAT_TIME64;
else
tu->tread = TREAD_FORMAT_TIME32;
if (tu->tread != old_tread &&
realloc_user_queue(tu, tu->queue_size) < 0) {
tu->tread = old_tread;
return -ENOMEM;
}
return 0;
}
enum { enum {
SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20), SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20),
SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21), SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21),
...@@ -1968,7 +2072,7 @@ enum { ...@@ -1968,7 +2072,7 @@ enum {
}; };
static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
unsigned long arg) unsigned long arg, bool compat)
{ {
struct snd_timer_user *tu; struct snd_timer_user *tu;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
...@@ -1980,23 +2084,9 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, ...@@ -1980,23 +2084,9 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0;
case SNDRV_TIMER_IOCTL_NEXT_DEVICE: case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
return snd_timer_user_next_device(argp); return snd_timer_user_next_device(argp);
case SNDRV_TIMER_IOCTL_TREAD: case SNDRV_TIMER_IOCTL_TREAD_OLD:
{ case SNDRV_TIMER_IOCTL_TREAD64:
int xarg, old_tread; return snd_timer_user_tread(argp, tu, cmd, compat);
if (tu->timeri) /* too late */
return -EBUSY;
if (get_user(xarg, p))
return -EFAULT;
old_tread = tu->tread;
tu->tread = xarg ? 1 : 0;
if (tu->tread != old_tread &&
realloc_user_queue(tu, tu->queue_size) < 0) {
tu->tread = old_tread;
return -ENOMEM;
}
return 0;
}
case SNDRV_TIMER_IOCTL_GINFO: case SNDRV_TIMER_IOCTL_GINFO:
return snd_timer_user_ginfo(file, argp); return snd_timer_user_ginfo(file, argp);
case SNDRV_TIMER_IOCTL_GPARAMS: case SNDRV_TIMER_IOCTL_GPARAMS:
...@@ -2009,8 +2099,10 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, ...@@ -2009,8 +2099,10 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
return snd_timer_user_info(file, argp); return snd_timer_user_info(file, argp);
case SNDRV_TIMER_IOCTL_PARAMS: case SNDRV_TIMER_IOCTL_PARAMS:
return snd_timer_user_params(file, argp); return snd_timer_user_params(file, argp);
case SNDRV_TIMER_IOCTL_STATUS: case SNDRV_TIMER_IOCTL_STATUS32:
return snd_timer_user_status(file, argp); return snd_timer_user_status32(file, argp);
case SNDRV_TIMER_IOCTL_STATUS64:
return snd_timer_user_status64(file, argp);
case SNDRV_TIMER_IOCTL_START: case SNDRV_TIMER_IOCTL_START:
case SNDRV_TIMER_IOCTL_START_OLD: case SNDRV_TIMER_IOCTL_START_OLD:
return snd_timer_user_start(file); return snd_timer_user_start(file);
...@@ -2034,7 +2126,7 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, ...@@ -2034,7 +2126,7 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
long ret; long ret;
mutex_lock(&tu->ioctl_lock); mutex_lock(&tu->ioctl_lock);
ret = __snd_timer_user_ioctl(file, cmd, arg); ret = __snd_timer_user_ioctl(file, cmd, arg, false);
mutex_unlock(&tu->ioctl_lock); mutex_unlock(&tu->ioctl_lock);
return ret; return ret;
} }
...@@ -2050,13 +2142,29 @@ static int snd_timer_user_fasync(int fd, struct file * file, int on) ...@@ -2050,13 +2142,29 @@ static int snd_timer_user_fasync(int fd, struct file * file, int on)
static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
size_t count, loff_t *offset) size_t count, loff_t *offset)
{ {
struct snd_timer_tread64 *tread;
struct snd_timer_tread32 tread32;
struct snd_timer_user *tu; struct snd_timer_user *tu;
long result = 0, unit; long result = 0, unit;
int qhead; int qhead;
int err = 0; int err = 0;
tu = file->private_data; tu = file->private_data;
unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read); switch (tu->tread) {
case TREAD_FORMAT_TIME64:
unit = sizeof(struct snd_timer_tread64);
break;
case TREAD_FORMAT_TIME32:
unit = sizeof(struct snd_timer_tread32);
break;
case TREAD_FORMAT_NONE:
unit = sizeof(struct snd_timer_read);
break;
default:
WARN_ONCE(1, "Corrupt snd_timer_user\n");
return -ENOTSUPP;
}
mutex_lock(&tu->ioctl_lock); mutex_lock(&tu->ioctl_lock);
spin_lock_irq(&tu->qlock); spin_lock_irq(&tu->qlock);
while ((long)count - result >= unit) { while ((long)count - result >= unit) {
...@@ -2095,14 +2203,34 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, ...@@ -2095,14 +2203,34 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
tu->qused--; tu->qused--;
spin_unlock_irq(&tu->qlock); spin_unlock_irq(&tu->qlock);
if (tu->tread) { tread = &tu->tqueue[qhead];
if (copy_to_user(buffer, &tu->tqueue[qhead],
sizeof(struct snd_timer_tread))) switch (tu->tread) {
case TREAD_FORMAT_TIME64:
if (copy_to_user(buffer, tread,
sizeof(struct snd_timer_tread64)))
err = -EFAULT; err = -EFAULT;
} else { break;
case TREAD_FORMAT_TIME32:
memset(&tread32, 0, sizeof(tread32));
tread32 = (struct snd_timer_tread32) {
.event = tread->event,
.tstamp_sec = tread->tstamp_sec,
.tstamp_sec = tread->tstamp_nsec,
.val = tread->val,
};
if (copy_to_user(buffer, &tread32, sizeof(tread32)))
err = -EFAULT;
break;
case TREAD_FORMAT_NONE:
if (copy_to_user(buffer, &tu->queue[qhead], if (copy_to_user(buffer, &tu->queue[qhead],
sizeof(struct snd_timer_read))) sizeof(struct snd_timer_read)))
err = -EFAULT; err = -EFAULT;
break;
default:
err = -ENOTSUPP;
break;
} }
spin_lock_irq(&tu->qlock); spin_lock_irq(&tu->qlock);
......
...@@ -69,54 +69,11 @@ static int snd_timer_user_info_compat(struct file *file, ...@@ -69,54 +69,11 @@ static int snd_timer_user_info_compat(struct file *file,
return 0; return 0;
} }
struct snd_timer_status32 {
struct compat_timespec tstamp;
u32 resolution;
u32 lost;
u32 overrun;
u32 queue;
unsigned char reserved[64];
};
static int snd_timer_user_status_compat(struct file *file,
struct snd_timer_status32 __user *_status)
{
struct snd_timer_user *tu;
struct snd_timer_status32 status;
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
memset(&status, 0, sizeof(status));
status.tstamp.tv_sec = tu->tstamp.tv_sec;
status.tstamp.tv_nsec = tu->tstamp.tv_nsec;
status.resolution = snd_timer_resolution(tu->timeri);
status.lost = tu->timeri->lost;
status.overrun = tu->overrun;
spin_lock_irq(&tu->qlock);
status.queue = tu->qused;
spin_unlock_irq(&tu->qlock);
if (copy_to_user(_status, &status, sizeof(status)))
return -EFAULT;
return 0;
}
#ifdef CONFIG_X86_X32
/* X32 ABI has the same struct as x86-64 */
#define snd_timer_user_status_x32(file, s) \
snd_timer_user_status(file, s)
#endif /* CONFIG_X86_X32 */
/*
*/
enum { enum {
SNDRV_TIMER_IOCTL_GPARAMS32 = _IOW('T', 0x04, struct snd_timer_gparams32), SNDRV_TIMER_IOCTL_GPARAMS32 = _IOW('T', 0x04, struct snd_timer_gparams32),
SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct snd_timer_info32), SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct snd_timer_info32),
SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct snd_timer_status32), SNDRV_TIMER_IOCTL_STATUS_COMPAT32 = _IOW('T', 0x14, struct snd_timer_status32),
#ifdef CONFIG_X86_X32 SNDRV_TIMER_IOCTL_STATUS_COMPAT64 = _IOW('T', 0x14, struct snd_timer_status64),
SNDRV_TIMER_IOCTL_STATUS_X32 = _IOW('T', 0x14, struct snd_timer_status),
#endif /* CONFIG_X86_X32 */
}; };
static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
...@@ -126,7 +83,8 @@ static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, ...@@ -126,7 +83,8 @@ static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
switch (cmd) { switch (cmd) {
case SNDRV_TIMER_IOCTL_PVERSION: case SNDRV_TIMER_IOCTL_PVERSION:
case SNDRV_TIMER_IOCTL_TREAD: case SNDRV_TIMER_IOCTL_TREAD_OLD:
case SNDRV_TIMER_IOCTL_TREAD64:
case SNDRV_TIMER_IOCTL_GINFO: case SNDRV_TIMER_IOCTL_GINFO:
case SNDRV_TIMER_IOCTL_GSTATUS: case SNDRV_TIMER_IOCTL_GSTATUS:
case SNDRV_TIMER_IOCTL_SELECT: case SNDRV_TIMER_IOCTL_SELECT:
...@@ -140,17 +98,15 @@ static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, ...@@ -140,17 +98,15 @@ static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
case SNDRV_TIMER_IOCTL_PAUSE: case SNDRV_TIMER_IOCTL_PAUSE:
case SNDRV_TIMER_IOCTL_PAUSE_OLD: case SNDRV_TIMER_IOCTL_PAUSE_OLD:
case SNDRV_TIMER_IOCTL_NEXT_DEVICE: case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
return __snd_timer_user_ioctl(file, cmd, (unsigned long)argp); return __snd_timer_user_ioctl(file, cmd, (unsigned long)argp, true);
case SNDRV_TIMER_IOCTL_GPARAMS32: case SNDRV_TIMER_IOCTL_GPARAMS32:
return snd_timer_user_gparams_compat(file, argp); return snd_timer_user_gparams_compat(file, argp);
case SNDRV_TIMER_IOCTL_INFO32: case SNDRV_TIMER_IOCTL_INFO32:
return snd_timer_user_info_compat(file, argp); return snd_timer_user_info_compat(file, argp);
case SNDRV_TIMER_IOCTL_STATUS32: case SNDRV_TIMER_IOCTL_STATUS_COMPAT32:
return snd_timer_user_status_compat(file, argp); return snd_timer_user_status32(file, argp);
#ifdef CONFIG_X86_X32 case SNDRV_TIMER_IOCTL_STATUS_COMPAT64:
case SNDRV_TIMER_IOCTL_STATUS_X32: return snd_timer_user_status64(file, argp);
return snd_timer_user_status_x32(file, argp);
#endif /* CONFIG_X86_X32 */
} }
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
......
...@@ -804,7 +804,7 @@ static void loopback_snd_timer_tasklet(unsigned long arg) ...@@ -804,7 +804,7 @@ static void loopback_snd_timer_tasklet(unsigned long arg)
static void loopback_snd_timer_event(struct snd_timer_instance *timeri, static void loopback_snd_timer_event(struct snd_timer_instance *timeri,
int event, int event,
struct timespec *tstamp, struct timespec64 *tstamp,
unsigned long resolution) unsigned long resolution)
{ {
/* Do not lock cable->lock here because timer->lock is already hold. /* Do not lock cable->lock here because timer->lock is already hold.
......
...@@ -487,7 +487,7 @@ static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime, ...@@ -487,7 +487,7 @@ static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime,
} }
static int azx_get_time_info(struct snd_pcm_substream *substream, static int azx_get_time_info(struct snd_pcm_substream *substream,
struct timespec *system_ts, struct timespec *audio_ts, struct timespec64 *system_ts, struct timespec64 *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report) struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
{ {
...@@ -507,7 +507,7 @@ static int azx_get_time_info(struct snd_pcm_substream *substream, ...@@ -507,7 +507,7 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
if (audio_tstamp_config->report_delay) if (audio_tstamp_config->report_delay)
nsec = azx_adjust_codec_delay(substream, nsec); nsec = azx_adjust_codec_delay(substream, nsec);
*audio_ts = ns_to_timespec(nsec); *audio_ts = ns_to_timespec64(nsec);
audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */ audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */
...@@ -524,16 +524,16 @@ static int azx_get_time_info(struct snd_pcm_substream *substream, ...@@ -524,16 +524,16 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
return -EINVAL; return -EINVAL;
case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW: case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
*system_ts = ktime_to_timespec(xtstamp.sys_monoraw); *system_ts = ktime_to_timespec64(xtstamp.sys_monoraw);
break; break;
default: default:
*system_ts = ktime_to_timespec(xtstamp.sys_realtime); *system_ts = ktime_to_timespec64(xtstamp.sys_realtime);
break; break;
} }
*audio_ts = ktime_to_timespec(xtstamp.device); *audio_ts = ktime_to_timespec64(xtstamp.device);
audio_tstamp_report->actual_type = audio_tstamp_report->actual_type =
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED; SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED;
......
...@@ -1258,7 +1258,7 @@ static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream, ...@@ -1258,7 +1258,7 @@ static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream,
static int skl_platform_soc_get_time_info( static int skl_platform_soc_get_time_info(
struct snd_soc_component *component, struct snd_soc_component *component,
struct snd_pcm_substream *substream, struct snd_pcm_substream *substream,
struct timespec *system_ts, struct timespec *audio_ts, struct timespec64 *system_ts, struct timespec64 *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report) struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
{ {
...@@ -1276,7 +1276,7 @@ static int skl_platform_soc_get_time_info( ...@@ -1276,7 +1276,7 @@ static int skl_platform_soc_get_time_info(
if (audio_tstamp_config->report_delay) if (audio_tstamp_config->report_delay)
nsec = skl_adjust_codec_delay(substream, nsec); nsec = skl_adjust_codec_delay(substream, nsec);
*audio_ts = ns_to_timespec(nsec); *audio_ts = ns_to_timespec64(nsec);
audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
audio_tstamp_report->accuracy_report = 1; /* rest of struct is valid */ audio_tstamp_report->accuracy_report = 1; /* rest of struct is valid */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment