From patchwork Tue Oct 14 08:29:31 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nick Stoughton X-Patchwork-Id: 5080591 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 4E7F5C11AC for ; Tue, 14 Oct 2014 14:32:58 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 75B01201C7 for ; Tue, 14 Oct 2014 14:32:53 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 18CC6200E9 for ; Tue, 14 Oct 2014 14:32:52 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 19B7A26069F; Tue, 14 Oct 2014 16:32:51 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Status: No, score=0.5 required=5.0 tests=BAYES_00, DATE_IN_PAST_06_12, DKIM_SIGNED, MSGID_FROM_MTA_HEADER, NO_DNS_FOR_FROM, RCVD_IN_SORBS_WEB, T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=no version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 919C92606AD; Tue, 14 Oct 2014 16:32:45 +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 DF4D42606AD; Tue, 14 Oct 2014 16:32:44 +0200 (CEST) Received: from mail-wg0-f51.google.com (mail-wg0-f51.google.com [74.125.82.51]) by alsa0.perex.cz (Postfix) with ESMTP id 20B772606B9 for ; Tue, 14 Oct 2014 16:32:30 +0200 (CEST) Received: by mail-wg0-f51.google.com with SMTP id b13so10852645wgh.34 for ; Tue, 14 Oct 2014 07:32:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aether.com; s=google; h=message-id:from:date:subject:to; bh=T4vsH4gb8wX8FnTWhLiuG5NvzL/dIYYgaLbV6Hig3zE=; b=F+QHvkoT+JvTKsJwvw6MjJDIEda6QhdHVzOCmzp8ZgLkAbx0ppWkfuhT8U0BU5/sAs V1YYRVwYue8AScAmCwd+t6GWhs0tuhuccAoFZXXo2JXY37N+fMXYLwBuaQCi5r4IDH3d XjZYvWVIsDK+EcfZnrjiW8SgvuZFAx4o4LQVQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:message-id:from:date:subject:to; bh=T4vsH4gb8wX8FnTWhLiuG5NvzL/dIYYgaLbV6Hig3zE=; b=BKpeYE/ZmpxglzkSGKFdbrMSWhZ9kyIAR7MVMnk0fm78b2gTmKkNFKFF43DxltAGlz 2fDSXZAn4/lVJt2csHaAGyDvCrO+Osl5jY3xLkLPGeEQ9Ou4u+h7kJsrLeHqi2VHmaBQ OwgC9hUTAshT2V3H0FLfPlD/Xizf/JkuKIaij4+j0mvawcO7jx0gQ+6couEdPDTQ3OZk sOIv7gfFZ/zO4i80IxSGrohprZ5k4ZIMUxcYGO2tCiu01vpx7qYrxVqZwNYg/RQNd0P5 YJxSQCQvHYpycFXzvs2bLk/GFr4dD+16xIcmVZ0TxQU/MpF7T0cP8SgNYO1wAXT5gP4i uhVQ== X-Gm-Message-State: ALoCoQmkYuDsGfiDN4f/PA/0PjMAEcfVZcjSO4MELuuwO5jDJ7vwrPLkvpgpTngryHWcGi0zUamg X-Received: by 10.180.39.241 with SMTP id s17mr5936907wik.35.1413297145381; Tue, 14 Oct 2014 07:32:25 -0700 (PDT) Received: from nstoughton@aether.com ([62.156.150.204]) by mx.google.com with ESMTPSA id fm10sm8261889wib.21.2014.10.14.07.32.23 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 14 Oct 2014 07:32:24 -0700 (PDT) Message-ID: <543d33f8.4a66b40a.638d.0583@mx.google.com> Received: by nstoughton@aether.com (sSMTP sendmail emulation); Tue, 14 Oct 2014 16:32:27 +0200 From: Nick Stoughton Date: Tue, 14 Oct 2014 10:29:31 +0200 To: alsa-devel@alsa-project.org Subject: [alsa-devel] [RFC] ALSA: provide a simple mechanism to start playback at a given time 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 Initial implementation / Request For Comment. Given an absolute time based on a given clock (CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW, CLOCK_REALTIME etc), setup a high resolution timer to cause playback to be triggered at that time. --- include/linux/timekeeping.h | 3 ++ include/uapi/sound/asound.h | 6 +++ kernel/time/timekeeping.c | 16 ++++++++ sound/core/pcm_native.c | 99 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+) diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 1caa6b0..74eb6b2 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -32,6 +32,9 @@ extern void ktime_get_ts64(struct timespec64 *ts); extern int __getnstimeofday64(struct timespec64 *tv); extern void getnstimeofday64(struct timespec64 *tv); +/* Get the offset between monotonic and monotonic raw clocks */ +extern ktime_t ktime_get_raw_offset(void); + #if BITS_PER_LONG == 64 static inline int __getnstimeofday(struct timespec *ts) { diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 6ee5867..c7aa88c 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -462,6 +462,11 @@ struct snd_xfern { snd_pcm_uframes_t frames; }; +struct snd_clock_time { + clockid_t clock; + struct timespec time; +}; + enum { SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0, /* gettimeofday equivalent */ SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, /* posix_clock_monotonic equivalent */ @@ -547,6 +552,7 @@ enum { #define SNDRV_PCM_IOCTL_READN_FRAMES _IOR('A', 0x53, struct snd_xfern) #define SNDRV_PCM_IOCTL_LINK _IOW('A', 0x60, int) #define SNDRV_PCM_IOCTL_UNLINK _IO('A', 0x61) +#define SNDRV_PCM_IOCTL_START_AT _IOR('A', 0x62, struct snd_clock_time) /***************************************************************************** * * diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index ec1791f..3d5d0bc 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -789,6 +789,22 @@ error: /* even if we error out, we forwarded the time, so call update */ } EXPORT_SYMBOL(timekeeping_inject_offset); +/** + * ktime_get_raw_offset - get the offset in ktime format between + * the monotonic_raw clock and the monotonic clock + */ +ktime_t ktime_get_raw_offset(void) +{ + struct timespec rawtime = {0,0}; + struct timespec now = {0,0}; + struct timespec delta; + + ktime_get_ts(&now); + getrawmonotonic(&rawtime); + delta = timespec_sub(now, rawtime); + return timespec_to_ktime(delta); +} +EXPORT_SYMBOL(ktime_get_raw_offset); /** * timekeeping_get_tai_offset - Returns current TAI offset from UTC diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 85fe1a2..1121127 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,9 @@ #if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) #include #endif +#if defined(CONFIG_HIGH_RES_TIMERS) +#include +#endif /* * Compatibility @@ -1016,6 +1020,99 @@ static struct action_ops snd_pcm_action_start = { .post_action = snd_pcm_post_start }; +#ifdef CONFIG_HIGH_RES_TIMERS +/* + * hrtimer interface + */ + +struct hrtimer_pcm { + struct hrtimer timer; + struct snd_pcm_substream *substream; +}; + +/* + * called from a hard irq context - no need for locks. + * only problem is that the caller might have gone away and closed the substream + * before the timer expires. + */ +enum hrtimer_restart snd_pcm_do_start_time(struct hrtimer *timer) +{ + struct hrtimer_pcm *pcm_timer; + struct snd_pcm_substream *substream; + int ret; + + pcm_timer = container_of(timer, struct hrtimer_pcm, timer); + substream = pcm_timer->substream; + + if (substream->runtime) { + ret = snd_pcm_do_start(substream, SNDRV_PCM_STATE_RUNNING); + if (ret == 0) { + snd_pcm_post_start(substream, SNDRV_PCM_STATE_RUNNING); + } + } + kfree(pcm_timer); + + return HRTIMER_NORESTART; +} + +int snd_pcm_start_at(struct snd_pcm_substream *substream, + struct snd_clock_time __user *_start_time) +{ + struct hrtimer_pcm *pcm_timer; + struct snd_clock_time start_time; + int clock; + ktime_t start_kt; + ktime_t raw_offset; + int ret; + + if (copy_from_user(&start_time, _start_time, sizeof(start_time))) + return -EFAULT; + + if (!timespec_valid(&start_time.time)) + return -EINVAL; + + start_kt = timespec_to_ktime(start_time.time); + clock = start_time.clock; + if (start_time.clock == CLOCK_MONOTONIC_RAW) { + raw_offset = ktime_get_raw_offset(); + + /* convert to clock monotonic */ + start_kt = ktime_add(raw_offset, start_kt); + clock = CLOCK_MONOTONIC; + } + + /* if not playback substream or less than 100 ms give up */ + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK || + (ktime_compare(start_kt, ktime_add_ns(ktime_get(), 100000000)) <= 0)) { + return snd_pcm_action_lock_irq(&snd_pcm_action_start, + substream, SNDRV_PCM_STATE_RUNNING); + } + + ret = snd_pcm_pre_start(substream, SNDRV_PCM_STATE_PREPARED); + if (ret != 0) { + return ret; + } + + pcm_timer = kmalloc(sizeof(*pcm_timer), GFP_KERNEL); + hrtimer_init(&pcm_timer->timer, clock, HRTIMER_MODE_ABS); + + pcm_timer->timer.function = snd_pcm_do_start_time; + pcm_timer->substream = substream; + + hrtimer_start(&pcm_timer->timer, start_kt, HRTIMER_MODE_ABS); + + return 0; +} +#else +/* without high res time, interface is identical to snd_pcm_start() */ +int snd_pcm_start_at(struct snd_pcm_substream *substream, + struct snd_clock_time __user *_start_time) +{ + return snd_pcm_action_lock_irq(&snd_pcm_action_start, + substream, SNDRV_PCM_STATE_RUNNING); +} +#endif + /** * snd_pcm_start - start all linked streams * @substream: the PCM substream instance @@ -2698,6 +2795,8 @@ static int snd_pcm_common_ioctl1(struct file *file, return snd_pcm_reset(substream); case SNDRV_PCM_IOCTL_START: return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING); + case SNDRV_PCM_IOCTL_START_AT: + return snd_pcm_start_at(substream, arg); case SNDRV_PCM_IOCTL_LINK: return snd_pcm_link(substream, (int)(unsigned long) arg); case SNDRV_PCM_IOCTL_UNLINK: