From patchwork Thu Sep 21 06:18:04 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "(Exiting) Baolin Wang" X-Patchwork-Id: 9963475 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 6874F601D5 for ; Thu, 21 Sep 2017 08:27:21 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 536AE288A3 for ; Thu, 21 Sep 2017 08:27:21 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 47A7D291AB; Thu, 21 Sep 2017 08:27:21 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-0.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_NONE, RCVD_IN_SORBS_SPAM, RDNS_NONE, T_DKIM_INVALID autolearn=no version=3.3.1 Received: from alsa0.perex.cz (unknown [77.48.224.243]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7D128288A3 for ; Thu, 21 Sep 2017 08:27:19 +0000 (UTC) Received: from alsa.alsa-project.org (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 26EFA2675D4; Thu, 21 Sep 2017 10:21:58 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 69627267588; Thu, 21 Sep 2017 08:18:41 +0200 (CEST) Received: from mail-pg0-f52.google.com (mail-pg0-f52.google.com [74.125.83.52]) by alsa0.perex.cz (Postfix) with ESMTP id D76FC267345 for ; Thu, 21 Sep 2017 08:18:38 +0200 (CEST) Received: by mail-pg0-f52.google.com with SMTP id v23so3014849pgc.5 for ; Wed, 20 Sep 2017 23:18:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=0Kkufujaam/tr38CMdCwWmU1UNiljhQNtrHmRhJl+v4=; b=Zl3WGWCURhINXeQBYLA90PzjvoBauSGqJFlJnGRA9C/tL55sbEme9+yXmA1Fqi582B Nx6S3wSzkN0WqDo71QdqwTFGQ4RPGo8Lvq+0wuXp4y+dcw+XLsWhvGFItayCJAcOPUT1 CicGLVcEVRxOYp+AeArLNSEWhKN01srxgnNFY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=0Kkufujaam/tr38CMdCwWmU1UNiljhQNtrHmRhJl+v4=; b=pe7kLdBZYwJbo43p3y1v/S2gO+U1OgJxpf3U+LTJHQ6Wbz1ksXjzNWjqeMlM4/93de /xzXPmouy7lmFHYpjdeBrGo194v5QV5SRuzDluqKPducbdyCZAsD0VquSRVN2qmAmdeR wtDafdUKVXDyx7M/I5V3a1RviYvkxclAOsCBVUd82WjrKvEDKKG1fPnvbEEsiJKyXA/I NVtF6DVFOfwBmCvOWtDYYMH3X58td6iKcVWl15gO/YmdJyXPi9inRL/tDlBSzgwWjVtn hPGb9vwbnh2vX/qDtjLD/g0N0hlTorgg8fRoSBz14/7IDmNoW1WhnXJ0t37f0+DFwDII vh5Q== X-Gm-Message-State: AHPjjUhQ+BNo7OgiqK5Dtv/pl5rpW/rsHN4g/hRRwLxIWiYazqUM8Y2y /huP4wOP/UPxSFEoiodnD4L8Yg== X-Google-Smtp-Source: AOwi7QCgjLje+sUuAzqdFIldAixHyZ8EDgdyGedUrLCBDQS3h9VufVh+O5u4uxJqJm+6a0pT0Skamg== X-Received: by 10.84.238.205 with SMTP id l13mr4423878pln.173.1505974717310; Wed, 20 Sep 2017 23:18:37 -0700 (PDT) Received: from baolinwangubtpc.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id q23sm1037773pfk.182.2017.09.20.23.18.31 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 20 Sep 2017 23:18:36 -0700 (PDT) From: Baolin Wang To: perex@perex.cz, tiwai@suse.com Date: Thu, 21 Sep 2017 14:18:04 +0800 Message-Id: <78aa803db47d99c2bee1a4dc8d426621324785b8.1505973912.git.baolin.wang@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: In-Reply-To: References: X-Mailman-Approved-At: Thu, 21 Sep 2017 10:19:53 +0200 Cc: mingo@kernel.org, fabf@skynet.be, arvind.yadav.cs@gmail.com, dharageswari.r@intel.com, linux-kernel@vger.kernel.org, arnd@arndb.de, baolin.wang@linaro.org, vinod.koul@intel.com, hardik.t.shah@intel.com, guneshwor.o.singh@intel.com, lgirdwood@gmail.com, o-takashi@sakamocchi.jp, gudishax.kranthikumar@intel.com, broonie@kernel.org, bhumirks@gmail.com, naveen.m@intel.com, jeeja.kp@intel.com, alsa-devel@alsa-project.org, elfring@users.sourceforge.net, dan.carpenter@oracle.com, deepa.kernel@gmail.com Subject: [alsa-devel] [RFC PATCH 2/7] sound: core: Avoid using timespec for struct snd_pcm_status X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP The struct snd_pcm_status will use 'timespec' type variables to record timestamp, which is not year 2038 safe on 32bits system. Userspace will use SNDRV_PCM_IOCTL_STATUS and SNDRV_PCM_IOCTL_STATUS_EXT as commands to issue ioctl() to fill the 'snd_pcm_status' structure in userspace. The command number is always defined through _IOR/_IOW/IORW, so when userspace changes the definition of 'struct timespec' to use 64-bit types, the command number also changes. Thus in the kernel, we now need to define two versions of each such ioctl and corresponding ioctl commands to handle 32bit time_t and 64bit time_t in native mode: struct snd_pcm_status32 { ...... struct { s32 tv_sec; s32 tv_nsec; } trigger_tstamp; struct { s32 tv_sec; s32 tv_nsec; } tstamp; ...... } struct snd_pcm_status64 { ...... struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp; struct { s64 tv_sec; s64 tv_nsec; } tstamp; ...... } Moreover in compat file, we renamed or introduced new structures to handle 32bit/64bit time_t in compatible mode. 'struct compat_snd_pcm_status32' and snd_pcm_status_user_compat() are used to handle 32bit time_t in compat mode. 'struct compat_snd_pcm_status64' and snd_pcm_status_user_compat64() are used to handle 64bit time_t with 64bit alignment. 'struct compat_snd_pcm_status64_x86_32' and snd_pcm_status_user_compat64_x86_32() are used to handle 64bit time_t with 32bit alignment. Finally we can replace SNDRV_PCM_IOCTL_STATUS and SNDRV_PCM_IOCTL_STATUS_EXT with new commands and introduce new functions to fill new 'struct snd_pcm_status64' instead of using unsafe 'struct snd_pcm_status'. Then in future, the new commands can be matched when userspace changes 'timespec' to 64bit type to make a size change of 'struct snd_pcm_status'. When glibc changes time_t to 64-bit, any recompiled program will issue ioctl commands that the kernel does not understand without this patch. Signed-off-by: Baolin Wang --- include/sound/pcm.h | 49 +++++++++- sound/core/pcm.c | 8 +- sound/core/pcm_compat.c | 238 ++++++++++++++++++++++++++++++++++++----------- sound/core/pcm_native.c | 108 +++++++++++++++++---- 4 files changed, 324 insertions(+), 79 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index cd1ecd6..114cc29 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -58,6 +58,7 @@ struct snd_pcm_hardware { size_t fifo_size; /* fifo size in bytes */ }; +struct snd_pcm_status64; struct snd_pcm_substream; struct snd_pcm_audio_tstamp_config; /* definitions further down */ @@ -565,8 +566,8 @@ struct snd_pcm_notify { int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info); int snd_pcm_info_user(struct snd_pcm_substream *substream, struct snd_pcm_info __user *info); -int snd_pcm_status(struct snd_pcm_substream *substream, - struct snd_pcm_status *status); +int snd_pcm_status64(struct snd_pcm_substream *substream, + struct snd_pcm_status64 *status); int snd_pcm_start(struct snd_pcm_substream *substream); int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status); int snd_pcm_drain_done(struct snd_pcm_substream *substream); @@ -1440,4 +1441,48 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format) #define pcm_dbg(pcm, fmt, args...) \ dev_dbg((pcm)->card->dev, fmt, ##args) +struct snd_pcm_status64 { + snd_pcm_state_t state; /* stream state */ + struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp; /* time when stream was started/stopped/paused */ + struct { s64 tv_sec; s64 tv_nsec; } tstamp; /* reference timestamp */ + snd_pcm_uframes_t appl_ptr; /* appl ptr */ + snd_pcm_uframes_t hw_ptr; /* hw ptr */ + snd_pcm_sframes_t delay; /* current delay in frames */ + snd_pcm_uframes_t avail; /* number of frames available */ + snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */ + snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ + snd_pcm_state_t suspended_state; /* suspended stream state */ + __u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */ + struct { s64 tv_sec; s64 tv_nsec; } audio_tstamp; /* sample counter, wall clock, PHC or on-demand sync'ed */ + struct { s64 tv_sec; s64 tv_nsec; } driver_tstamp; /* useful in case reference system tstamp is reported with delay */ + __u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */ + unsigned char reserved[52-2*sizeof(struct { s64 tv_sec; s64 tv_nsec; })]; /* must be filled with zero */ +}; + +#define SNDRV_PCM_IOCTL_STATUS64 _IOR('A', 0x20, struct snd_pcm_status64) +#define SNDRV_PCM_IOCTL_STATUS_EXT64 _IOWR('A', 0x24, struct snd_pcm_status64) + +#if __BITS_PER_LONG == 32 +struct snd_pcm_status32 { + snd_pcm_state_t state; /* stream state */ + struct { s32 tv_sec; s32 tv_nsec; } trigger_tstamp; /* time when stream was started/stopped/paused */ + struct { s32 tv_sec; s32 tv_nsec; } tstamp; /* reference timestamp */ + snd_pcm_uframes_t appl_ptr; /* appl ptr */ + snd_pcm_uframes_t hw_ptr; /* hw ptr */ + snd_pcm_sframes_t delay; /* current delay in frames */ + snd_pcm_uframes_t avail; /* number of frames available */ + snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */ + snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ + snd_pcm_state_t suspended_state; /* suspended stream state */ + __u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */ + struct { s32 tv_sec; s32 tv_nsec; } audio_tstamp; /* sample counter, wall clock, PHC or on-demand sync'ed */ + struct { s32 tv_sec; s32 tv_nsec; } driver_tstamp; /* useful in case reference system tstamp is reported with delay */ + __u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */ + unsigned char reserved[52-2*sizeof(struct { s32 tv_sec; s32 tv_nsec; })]; /* must be filled with zero */ +}; + +#define SNDRV_PCM_IOCTL_STATUS32 _IOR('A', 0x20, struct snd_pcm_status32) +#define SNDRV_PCM_IOCTL_STATUS_EXT32 _IOWR('A', 0x24, struct snd_pcm_status32) +#endif + #endif /* __SOUND_PCM_H */ diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 7eadb7f..2d990d9 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -453,7 +453,7 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, { struct snd_pcm_substream *substream = entry->private_data; struct snd_pcm_runtime *runtime; - struct snd_pcm_status status; + struct snd_pcm_status64 status; int err; mutex_lock(&substream->pcm->open_mutex); @@ -463,16 +463,16 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, goto unlock; } memset(&status, 0, sizeof(status)); - err = snd_pcm_status(substream, &status); + err = snd_pcm_status64(substream, &status); if (err < 0) { snd_iprintf(buffer, "error %d\n", err); goto unlock; } snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid)); - snd_iprintf(buffer, "trigger_time: %ld.%09ld\n", + snd_iprintf(buffer, "trigger_time: %lld.%09lld\n", status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec); - snd_iprintf(buffer, "tstamp : %ld.%09ld\n", + snd_iprintf(buffer, "tstamp : %lld.%09lld\n", status.tstamp.tv_sec, status.tstamp.tv_nsec); snd_iprintf(buffer, "delay : %ld\n", status.delay); snd_iprintf(buffer, "avail : %ld\n", status.avail); diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index b719d0b..79e7475 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -187,7 +187,7 @@ static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream, snd_pcm_channel_info_user(s, p) #endif /* CONFIG_X86_X32 */ -struct snd_pcm_status32 { +struct compat_snd_pcm_status32 { s32 state; struct compat_timespec trigger_tstamp; struct compat_timespec tstamp; @@ -207,13 +207,15 @@ struct snd_pcm_status32 { static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, - struct snd_pcm_status32 __user *src, + struct compat_snd_pcm_status32 __user *src, bool ext) { - struct snd_pcm_status status; + struct snd_pcm_status64 status; + struct compat_snd_pcm_status32 compat_status32; int err; memset(&status, 0, sizeof(status)); + memset(&compat_status32, 0, sizeof(compat_status32)); /* * with extension, parameters are read/write, * get audio_tstamp_data from user, @@ -222,38 +224,53 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, if (ext && get_user(status.audio_tstamp_data, (u32 __user *)(&src->audio_tstamp_data))) return -EFAULT; - err = snd_pcm_status(substream, &status); + err = snd_pcm_status64(substream, &status); if (err < 0) return err; if (clear_user(src, sizeof(*src))) return -EFAULT; - if (put_user(status.state, &src->state) || - compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) || - compat_put_timespec(&status.tstamp, &src->tstamp) || - put_user(status.appl_ptr, &src->appl_ptr) || - put_user(status.hw_ptr, &src->hw_ptr) || - put_user(status.delay, &src->delay) || - put_user(status.avail, &src->avail) || - put_user(status.avail_max, &src->avail_max) || - put_user(status.overrange, &src->overrange) || - put_user(status.suspended_state, &src->suspended_state) || - put_user(status.audio_tstamp_data, &src->audio_tstamp_data) || - compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) || - compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) || - put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy)) + + compat_status32 = (struct compat_snd_pcm_status32) { + .state = status.state, + .trigger_tstamp = { + .tv_sec = status.trigger_tstamp.tv_sec, + .tv_nsec = status.trigger_tstamp.tv_nsec, + }, + .tstamp = { + .tv_sec = status.tstamp.tv_sec, + .tv_nsec = status.tstamp.tv_nsec, + }, + .appl_ptr = status.appl_ptr, + .hw_ptr = status.hw_ptr, + .delay = status.delay, + .avail = status.avail, + .avail_max = status.avail_max, + .overrange = status.overrange, + .suspended_state = status.suspended_state, + .audio_tstamp_data = status.audio_tstamp_data, + .audio_tstamp = { + .tv_sec = status.audio_tstamp.tv_sec, + .tv_nsec = status.audio_tstamp.tv_nsec, + }, + .driver_tstamp = { + .tv_sec = status.audio_tstamp.tv_sec, + .tv_nsec = status.audio_tstamp.tv_nsec, + }, + .audio_tstamp_accuracy = status.audio_tstamp_accuracy, + }; + + if (copy_to_user(src, &compat_status32, sizeof(compat_status32))) return -EFAULT; return err; } -#ifdef CONFIG_X86_X32 -/* X32 ABI has 64bit timespec and 64bit alignment */ -struct snd_pcm_status_x32 { +struct compat_snd_pcm_status64 { s32 state; u32 rsvd; /* alignment */ - struct timespec trigger_tstamp; - struct timespec tstamp; + struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp; + struct { s64 tv_sec; s64 tv_nsec; } tstamp; u32 appl_ptr; u32 hw_ptr; s32 delay; @@ -262,22 +279,24 @@ struct snd_pcm_status_x32 { u32 overrange; s32 suspended_state; u32 audio_tstamp_data; - struct timespec audio_tstamp; - struct timespec driver_tstamp; + struct { s64 tv_sec; s64 tv_nsec; } audio_tstamp; + struct { s64 tv_sec; s64 tv_nsec; } driver_tstamp; u32 audio_tstamp_accuracy; - unsigned char reserved[52-2*sizeof(struct timespec)]; + unsigned char reserved[52-2*sizeof(struct { s64 tv_sec; s64 tv_nsec; })]; } __packed; #define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst)) -static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream, - struct snd_pcm_status_x32 __user *src, - bool ext) +static int snd_pcm_status_user_compat64(struct snd_pcm_substream *substream, + struct compat_snd_pcm_status64 __user *src, + bool ext) { - struct snd_pcm_status status; + struct snd_pcm_status64 status; + struct compat_snd_pcm_status64 compat_status64; int err; memset(&status, 0, sizeof(status)); + memset(&compat_status64, 0, sizeof(compat_status64)); /* * with extension, parameters are read/write, * get audio_tstamp_data from user, @@ -286,31 +305,128 @@ static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream, if (ext && get_user(status.audio_tstamp_data, (u32 __user *)(&src->audio_tstamp_data))) return -EFAULT; - err = snd_pcm_status(substream, &status); + err = snd_pcm_status64(substream, &status); if (err < 0) return err; if (clear_user(src, sizeof(*src))) return -EFAULT; - if (put_user(status.state, &src->state) || - put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) || - put_timespec(&status.tstamp, &src->tstamp) || - put_user(status.appl_ptr, &src->appl_ptr) || - put_user(status.hw_ptr, &src->hw_ptr) || - put_user(status.delay, &src->delay) || - put_user(status.avail, &src->avail) || - put_user(status.avail_max, &src->avail_max) || - put_user(status.overrange, &src->overrange) || - put_user(status.suspended_state, &src->suspended_state) || - put_user(status.audio_tstamp_data, &src->audio_tstamp_data) || - put_timespec(&status.audio_tstamp, &src->audio_tstamp) || - put_timespec(&status.driver_tstamp, &src->driver_tstamp) || - put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy)) + + compat_status64 = (struct compat_snd_pcm_status64) { + .state = status.state, + .trigger_tstamp = { + .tv_sec = status.trigger_tstamp.tv_sec, + .tv_nsec = status.trigger_tstamp.tv_nsec, + }, + .tstamp = { + .tv_sec = status.tstamp.tv_sec, + .tv_nsec = status.tstamp.tv_nsec, + }, + .appl_ptr = status.appl_ptr, + .hw_ptr = status.hw_ptr, + .delay = status.delay, + .avail = status.avail, + .avail_max = status.avail_max, + .overrange = status.overrange, + .suspended_state = status.suspended_state, + .audio_tstamp_data = status.audio_tstamp_data, + .audio_tstamp = { + .tv_sec = status.audio_tstamp.tv_sec, + .tv_nsec = status.audio_tstamp.tv_nsec, + }, + .driver_tstamp = { + .tv_sec = status.audio_tstamp.tv_sec, + .tv_nsec = status.audio_tstamp.tv_nsec, + }, + .audio_tstamp_accuracy = status.audio_tstamp_accuracy, + }; + + if (copy_to_user(src, &compat_status64, sizeof(compat_status64))) return -EFAULT; return err; } -#endif /* CONFIG_X86_X32 */ + +#ifdef IA32_EMULATION +struct compat_snd_pcm_status64_x86_32 { + s32 state; + struct { s64 tv_sec; s64 tv_nsec; } trigger_tstamp; + struct { s64 tv_sec; s64 tv_nsec; } tstamp; + u32 appl_ptr; + u32 hw_ptr; + s32 delay; + u32 avail; + u32 avail_max; + u32 overrange; + s32 suspended_state; + u32 audio_tstamp_data; + struct { s64 tv_sec; s64 tv_nsec; } audio_tstamp; + struct { s64 tv_sec; s64 tv_nsec; } driver_tstamp; + u32 audio_tstamp_accuracy; + unsigned char reserved[52-2*sizeof(struct { s64 tv_sec; s64 tv_nsec; })]; +} __packed; + +static int +snd_pcm_status_user_compat64_x86_32(struct snd_pcm_substream *substream, + struct compat_snd_pcm_status64_x86_32 __user *src, + bool ext) +{ + struct snd_pcm_status64 status; + struct compat_snd_pcm_status64_x86_32 status_x86_32; + int err; + + memset(&status, 0, sizeof(status)); + memset(&status_x86_32, 0, sizeof(status_x86_32)); + /* + * with extension, parameters are read/write, + * get audio_tstamp_data from user, + * ignore rest of status structure + */ + if (ext && get_user(status.audio_tstamp_data, + (u32 __user *)(&src->audio_tstamp_data))) + return -EFAULT; + err = snd_pcm_status64(substream, &status); + if (err < 0) + return err; + + if (clear_user(src, sizeof(*src))) + return -EFAULT; + + status_x86_32 = (struct compat_snd_pcm_status64_x86_32) { + .state = status.state, + .trigger_tstamp = { + .tv_sec = status.trigger_tstamp.tv_sec, + .tv_nsec = status.trigger_tstamp.tv_nsec, + }, + .tstamp = { + .tv_sec = status.tstamp.tv_sec, + .tv_nsec = status.tstamp.tv_nsec, + }, + .appl_ptr = status.appl_ptr, + .hw_ptr = status.hw_ptr, + .delay = status.delay, + .avail = status.avail, + .avail_max = status.avail_max, + .overrange = status.overrange, + .suspended_state = status.suspended_state, + .audio_tstamp_data = status.audio_tstamp_data, + .audio_tstamp = { + .tv_sec = status.audio_tstamp.tv_sec, + .tv_nsec = status.audio_tstamp.tv_nsec, + }, + .driver_tstamp = { + .tv_sec = status.audio_tstamp.tv_sec, + .tv_nsec = status.audio_tstamp.tv_nsec, + }, + .audio_tstamp_accuracy = status.audio_tstamp_accuracy, + }; + + if (copy_to_user(src, &status_x86_32, sizeof(status_x86_32))) + return -EFAULT; + + return err; +} +#endif /* both for HW_PARAMS and HW_REFINE */ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, @@ -633,8 +749,8 @@ enum { SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32), SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32), SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32), - SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32), - SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32), + SNDRV_PCM_IOCTL_STATUS_COMPAT32 = _IOR('A', 0x20, struct compat_snd_pcm_status32), + SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32 = _IOWR('A', 0x24, struct compat_snd_pcm_status32), SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32), SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32), SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), @@ -644,10 +760,14 @@ enum { SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32), SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32), SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32), + SNDRV_PCM_IOCTL_STATUS_COMPAT64 = _IOR('A', 0x20, struct compat_snd_pcm_status64), + SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64 = _IOWR('A', 0x24, struct compat_snd_pcm_status64), +#ifdef IA32_EMULATION + SNDRV_PCM_IOCTL_STATUS_COMPAT64_X86_32 = _IOR('A', 0x20, struct compat_snd_pcm_status64_x86_32), + SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64_X86_32 = _IOWR('A', 0x24, struct compat_snd_pcm_status64_x86_32), +#endif #ifdef CONFIG_X86_X32 SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info), - SNDRV_PCM_IOCTL_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32), - SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32), SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32), #endif /* CONFIG_X86_X32 */ }; @@ -697,9 +817,9 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l return snd_pcm_ioctl_hw_params_compat(substream, 0, argp); case SNDRV_PCM_IOCTL_SW_PARAMS32: return snd_pcm_ioctl_sw_params_compat(substream, argp); - case SNDRV_PCM_IOCTL_STATUS32: + case SNDRV_PCM_IOCTL_STATUS_COMPAT32: return snd_pcm_status_user_compat(substream, argp, false); - case SNDRV_PCM_IOCTL_STATUS_EXT32: + case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32: return snd_pcm_status_user_compat(substream, argp, true); case SNDRV_PCM_IOCTL_SYNC_PTR32: return snd_pcm_ioctl_sync_ptr_compat(substream, argp); @@ -719,11 +839,17 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l return snd_pcm_ioctl_rewind_compat(substream, argp); case SNDRV_PCM_IOCTL_FORWARD32: return snd_pcm_ioctl_forward_compat(substream, argp); + case SNDRV_PCM_IOCTL_STATUS_COMPAT64: + return snd_pcm_status_user_compat64(substream, argp, false); + case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64: + return snd_pcm_status_user_compat64(substream, argp, true); +#ifdef IA32_EMULATION + case SNDRV_PCM_IOCTL_STATUS_COMPAT64_X86_32: + return snd_pcm_status_user_compat64_x86_32(substream, argp, false); + case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64_X86_32: + return snd_pcm_status_user_compat64_x86_32(substream, argp, true); +#endif #ifdef CONFIG_X86_X32 - case SNDRV_PCM_IOCTL_STATUS_X32: - return snd_pcm_status_user_x32(substream, argp, false); - case SNDRV_PCM_IOCTL_STATUS_EXT_X32: - return snd_pcm_status_user_x32(substream, argp, true); case SNDRV_PCM_IOCTL_SYNC_PTR_X32: return snd_pcm_ioctl_sync_ptr_x32(substream, argp); case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32: diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 60bc303..7f1f60d 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -854,8 +854,8 @@ static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream, return err; } -int snd_pcm_status(struct snd_pcm_substream *substream, - struct snd_pcm_status *status) +int snd_pcm_status64(struct snd_pcm_substream *substream, + struct snd_pcm_status64 *status) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -881,14 +881,22 @@ int snd_pcm_status(struct snd_pcm_substream *substream, status->suspended_state = runtime->status->suspended_state; if (status->state == SNDRV_PCM_STATE_OPEN) goto _end; - status->trigger_tstamp = timespec64_to_timespec(runtime->trigger_tstamp); + status->trigger_tstamp.tv_sec = runtime->trigger_tstamp.tv_sec; + status->trigger_tstamp.tv_nsec = runtime->trigger_tstamp.tv_nsec; if (snd_pcm_running(substream)) { snd_pcm_update_hw_ptr(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { - status->tstamp = runtime->status->tstamp; - status->driver_tstamp = timespec64_to_timespec(runtime->driver_tstamp); - status->audio_tstamp = - runtime->status->audio_tstamp; + status->tstamp.tv_sec = runtime->status->tstamp.tv_sec; + status->tstamp.tv_nsec = + runtime->status->tstamp.tv_nsec; + status->driver_tstamp.tv_sec = + runtime->driver_tstamp.tv_sec; + status->driver_tstamp.tv_nsec = + runtime->driver_tstamp.tv_nsec; + status->audio_tstamp.tv_sec = + runtime->status->audio_tstamp.tv_sec; + status->audio_tstamp.tv_nsec = + runtime->status->audio_tstamp.tv_nsec; if (runtime->audio_tstamp_report.valid == 1) /* backwards compatibility, no report provided in COMPAT mode */ snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data, @@ -903,7 +911,8 @@ int snd_pcm_status(struct snd_pcm_substream *substream, struct timespec64 tstamp; snd_pcm_gettime(runtime, &tstamp); - status->tstamp = timespec64_to_timespec(tstamp); + status->tstamp.tv_sec = tstamp.tv_sec; + status->tstamp.tv_nsec = tstamp.tv_nsec; } } _tstamp_end: @@ -933,11 +942,11 @@ int snd_pcm_status(struct snd_pcm_substream *substream, return 0; } -static int snd_pcm_status_user(struct snd_pcm_substream *substream, - struct snd_pcm_status __user * _status, - bool ext) +static int snd_pcm_status_user64(struct snd_pcm_substream *substream, + struct snd_pcm_status64 __user * _status, + bool ext) { - struct snd_pcm_status status; + struct snd_pcm_status64 status; int res; memset(&status, 0, sizeof(status)); @@ -949,7 +958,7 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream, if (ext && get_user(status.audio_tstamp_data, (u32 __user *)(&_status->audio_tstamp_data))) return -EFAULT; - res = snd_pcm_status(substream, &status); + res = snd_pcm_status64(substream, &status); if (res < 0) return res; if (copy_to_user(_status, &status, sizeof(status))) @@ -957,6 +966,65 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream, return 0; } +#if __BITS_PER_LONG == 32 +static int snd_pcm_status_user32(struct snd_pcm_substream *substream, + struct snd_pcm_status32 __user * _status, + bool ext) +{ + struct snd_pcm_status64 status64; + struct snd_pcm_status32 status32; + int res; + + memset(&status64, 0, sizeof(status64)); + memset(&status32, 0, sizeof(status32)); + /* + * with extension, parameters are read/write, + * get audio_tstamp_data from user, + * ignore rest of status structure + */ + if (ext && get_user(status64.audio_tstamp_data, + (u32 __user *)(&_status->audio_tstamp_data))) + return -EFAULT; + res = snd_pcm_status64(substream, &status64); + if (res < 0) + return res; + + status32 = (struct snd_pcm_status32) { + .state = status64.state, + .trigger_tstamp = { + .tv_sec = status64.trigger_tstamp.tv_sec, + .tv_nsec = status64.trigger_tstamp.tv_nsec, + }, + .tstamp = { + .tv_sec = status64.tstamp.tv_sec, + .tv_nsec = status64.tstamp.tv_nsec, + }, + .appl_ptr = status64.appl_ptr, + .hw_ptr = status64.hw_ptr, + .delay = status64.delay, + .avail = status64.avail, + .avail_max = status64.avail_max, + .overrange = status64.overrange, + .suspended_state = status64.suspended_state, + .audio_tstamp_data = status64.audio_tstamp_data, + .audio_tstamp = { + .tv_sec = status64.audio_tstamp.tv_sec, + .tv_nsec = status64.audio_tstamp.tv_nsec, + }, + .driver_tstamp = { + .tv_sec = status64.audio_tstamp.tv_sec, + .tv_nsec = status64.audio_tstamp.tv_nsec, + }, + .audio_tstamp_accuracy = status64.audio_tstamp_accuracy, + }; + + if (copy_to_user(_status, &status32, sizeof(status32))) + return -EFAULT; + + return 0; +} +#endif + static int snd_pcm_channel_info(struct snd_pcm_substream *substream, struct snd_pcm_channel_info * info) { @@ -2888,10 +2956,16 @@ static int snd_pcm_common_ioctl(struct file *file, return snd_pcm_hw_free(substream); case SNDRV_PCM_IOCTL_SW_PARAMS: return snd_pcm_sw_params_user(substream, arg); - case SNDRV_PCM_IOCTL_STATUS: - return snd_pcm_status_user(substream, arg, false); - case SNDRV_PCM_IOCTL_STATUS_EXT: - return snd_pcm_status_user(substream, arg, true); +#if __BITS_PER_LONG == 32 + case SNDRV_PCM_IOCTL_STATUS32: + return snd_pcm_status_user32(substream, arg, false); + case SNDRV_PCM_IOCTL_STATUS_EXT32: + return snd_pcm_status_user32(substream, arg, true); +#endif + case SNDRV_PCM_IOCTL_STATUS64: + return snd_pcm_status_user64(substream, arg, false); + case SNDRV_PCM_IOCTL_STATUS_EXT64: + return snd_pcm_status_user64(substream, arg, true); case SNDRV_PCM_IOCTL_CHANNEL_INFO: return snd_pcm_channel_info_user(substream, arg); case SNDRV_PCM_IOCTL_PREPARE: