All of lore.kernel.org
 help / color / mirror / Atom feed
From: Takashi Iwai <tiwai@suse.de>
To: Tim Cussins <timcussins@eml.cc>
Cc: Nick Stoughton <nstoughton@aether.com>, alsa-devel@alsa-project.org
Subject: Re: [RFC] ALSA: provide a simple mechanism to start playback at a given time
Date: Thu, 16 Oct 2014 16:34:49 +0200	[thread overview]
Message-ID: <s5hwq80ulpy.wl-tiwai@suse.de> (raw)
In-Reply-To: <543FA393.7020605@eml.cc>

At Thu, 16 Oct 2014 11:53:07 +0100,
Tim Cussins wrote:
> 
> Hi again Nick!
> 
> On 14/10/14 09:29, Nick Stoughton wrote:
> > 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;
> > +};
> > +
> 
> Takashi added snd_pcm_tstamp_type_t in July - If the final start_at 
> patch includes some "struct snd_clock_time", then it'd probably use 
> Takashi's type in favour of a raw clockid_t.

Yeah, that would make things more consistent.  But...

> I like the idea of a single struct that includes the context for the 
> timespec btw. Cool.

... thinking of this again, can we drop specifying the type here but
assumes to be same as the pre-given timestamp type?  Then we'd just
need to pass timespec to ioctl.

And, if we introduce a new PTP or DEVICE_SPECIFIC timestamp type as
we've discussed, these need to provide the wakeup / callback at the
programmed time.  In the latter case, the device driver should provide
it (hmm, so the start_at() would end up calling the callback?)  This
needs to be sorted out a bit better...

> >   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);

This part needs a review and comment from Thomas and others.
We can have this code snippet even in the PCM code without disturbing
the core side.


> >    * 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 <linux/pm_qos.h>
> >   #include <linux/aio.h>
> >   #include <linux/dma-mapping.h>
> > +#include <linux/time.h>
> >   #include <sound/core.h>
> >   #include <sound/control.h>
> >   #include <sound/info.h>
> > @@ -38,6 +39,9 @@
> >   #if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
> >   #include <dma-coherence.h>
> >   #endif
> > +#if defined(CONFIG_HIGH_RES_TIMERS)
> > +#include <linux/hrtimer.h>
> > +#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 */

Is there any technical reason to give up for over longer schedule?


> > +	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
> > +
> 
> I'd prefer to return an error code when snd_pcm_start_at cannot be 
> supported, rather than lying to userspace that it will be satisfied :)
> 
> There might be a number of reasons that snd_pcm_start_at cannot be 
> satisfied: unsupported clock-type, timespec in the past, incompatible 
> kernel config are just three.
> 
> While one might argue that start_at is a "best effort" mechanism, on 
> principle the function should make every effort to return an error when 
> it would be meaningful (read: handleable) by userspace. Not sure how you 
> feel about that ... :)

Yeah, I second that, too.  (BTW, the function should be static.)


thanks,

Takashi

      reply	other threads:[~2014-10-16 14:34 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-10-14  8:29 [RFC] ALSA: provide a simple mechanism to start playback at a given time Nick Stoughton
2014-10-16 10:53 ` Tim Cussins
2014-10-16 14:34   ` Takashi Iwai [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=s5hwq80ulpy.wl-tiwai@suse.de \
    --to=tiwai@suse.de \
    --cc=alsa-devel@alsa-project.org \
    --cc=nstoughton@aether.com \
    --cc=timcussins@eml.cc \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.