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/#m6519cb07cfda08adf1dedea6596bb98892b4d5dc Signed-off-by: Arnd 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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJd+URTAAoJEGCrR//JCVIn4FwQAJlo8vLvc/bZc58cw5KBdsal /lgYiCoT2Q7IMC3FHr28jVd+R3vAzfSMJFmwawCtV0iNXOsAHv2yyE9+whqH0ljg oz1o7X+oDBBa9Fed1WIxrAjFWzI7u+4lz29qvkenP6x2h9tvvMq0cdxRMz4qSVPk u7c7Yui/yEtBHmcWoQFNhIpeTFg8QCPZnR9SkyZ4O/q1GS9n+Ep5kLioFl3PQGGi KndZ+gMBgIj4l7sRV0l2KFzP/N2dXowUlP+AW9V80LgSI4VyQ7jgMG+QalgzAXwH ey1apJ38m51EglAi4TTglcTpe/TyTKLHs9JBAI+7QNa90EwJTrmjmXwXtfnBVlAS d0uK7ISFNyK/PujYsUD7FQrDeubgSeAYHyKtAh3YGVKFUEOBsGywSVyuCIoRJsmR 2mJFHrsZnvKyooGJY9gwMmttoanwcXnGXloTjFQF7qEzFoi2BdoBsjWrDiN+3olc WVbW3rWSjVeKzD9cEylchTxkxFP1j2NDQEPD4epq+ncm0feq9mC7rrt5G9gXKMmu qf2pMEUKFE7Tn3LllT4iUY7z4FybgEuKADH5/5zQjsVvBZ5BCvi1BDUJaYRQozxh hC6ovFG7uTRL6wOw/7GGx9/lgX2GQ91z6IJeuqZSLj+tdlzVKoScfRgnrbZS8ZOy W4RfCyuo5PedyqfTeTEX =58V0 -----END PGP SIGNATURE----- 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/#m6519cb07cfda08adf1dedea6596bb98892b4d5dc Signed-off-by: Arnd 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.
This commit is contained in:
Коммит
df1d6ea05a
|
@ -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 {
|
||||||
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
|
||||||
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 {
|
||||||
/* -- 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);
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
#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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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 @@
|
||||||
#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 {
|
||||||
* *
|
* *
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#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;
|
||||||
#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;
|
||||||
|
|
||||||
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 {
|
||||||
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 {
|
||||||
__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
|
||||||
|
|
||||||
struct snd_pcm_mmap_status {
|
/*
|
||||||
|
* 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 {
|
||||||
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 {
|
||||||
#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 {
|
||||||
#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 {
|
||||||
* 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 {
|
||||||
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 {
|
||||||
* 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
#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 {
|
||||||
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 {
|
||||||
} 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,
|
||||||
{
|
{
|
||||||
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,
|
||||||
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 {
|
||||||
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,
|
||||||
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 {
|
||||||
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,
|
||||||
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,
|
||||||
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 {
|
||||||
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,
|
||||||
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 {
|
||||||
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 {
|
||||||
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
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
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
|
||||||
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
|
||||||
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)
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
/* 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 @@
|
||||||
* 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)
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
} 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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
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);
|
||||||
#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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
|
.tstamp_nsec = status.tstamp_nsec,
|
||||||
|
.avail = status.avail,
|
||||||
|
.xruns = status.xruns,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (copy_to_user(src, &compat_status, sizeof(*src)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#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) ||
|
|
||||||
put_user(status.avail, &src->avail) ||
|
|
||||||
put_user(status.xruns, &src->xruns))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
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 {
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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_user *tu;
|
||||||
struct snd_timer_status status;
|
struct snd_timer_status32 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.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_status64 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.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)
|
||||||
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 {
|
||||||
};
|
};
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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(
|
||||||
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 */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче