All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 1/3] snd_pcm_start_at: Add enum for timestamp class, and helper struct for start_at ioctl.
@ 2014-12-17 17:29 Tim Cussins
  2014-12-17 17:29 ` [PATCH v2 2/3] snd_pcm_start_at: Add snd_pcm_ops so that driver can handle start_at requests Tim Cussins
  2014-12-17 17:29 ` [PATCH v2 3/3] snd_pcm_start_at: Implement snd_pcm_start_at for system and audio tstamp types Tim Cussins
  0 siblings, 2 replies; 5+ messages in thread
From: Tim Cussins @ 2014-12-17 17:29 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, nstoughton, Tim Cussins, pierre-louis.bossart

As per Pierre's suggestion, timestamp types are of two types:
System, and Audio.

Signed-off-by: Tim Cussins <timcussins@eml.cc>

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 1f23cd6..bd1e9be 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -466,12 +466,30 @@ struct snd_xfern {
 };
 
 enum {
+        SNDRV_PCM_TSTAMP_CLASS_SYSTEM = 0,
+        SNDRV_PCM_TSTAMP_CLASS_AUDIO,
+        SNDRV_PCM_TSTAMP_CLASS_LAST = SNDRV_PCM_TSTAMP_CLASS_AUDIO,
+};
+
+enum {
 	SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0,	/* gettimeofday equivalent */
 	SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,	/* posix_clock_monotonic equivalent */
 	SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW,    /* monotonic_raw (no NTP) */
 	SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW,
 };
 
+enum {
+        SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT = 0,
+        SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK,
+        SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK,
+};
+
+struct snd_start_at {
+        int tstamp_class;
+        int tstamp_type;
+        struct timespec start_time;
+};
+
 /* channel positions */
 enum {
 	SNDRV_CHMAP_UNKNOWN = 0,
@@ -550,6 +568,8 @@ 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        _IOW('A', 0x62, struct snd_start_at)
+
 
 /*****************************************************************************
  *                                                                           *
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 2/3] snd_pcm_start_at: Add snd_pcm_ops so that driver can handle start_at requests.
  2014-12-17 17:29 [PATCH v2 1/3] snd_pcm_start_at: Add enum for timestamp class, and helper struct for start_at ioctl Tim Cussins
@ 2014-12-17 17:29 ` Tim Cussins
  2014-12-17 17:29 ` [PATCH v2 3/3] snd_pcm_start_at: Implement snd_pcm_start_at for system and audio tstamp types Tim Cussins
  1 sibling, 0 replies; 5+ messages in thread
From: Tim Cussins @ 2014-12-17 17:29 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, nstoughton, Tim Cussins, pierre-louis.bossart

Provide members for starting and cancelling the timer.
Add state to snd_pcm_runtime that tracks current timer: This
allows us to cancel on state-change, or subsequent start_at call.

Signed-off-by: Tim Cussins <timcussins@eml.cc>

diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 1e7f74a..e7d5ded 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -73,6 +73,8 @@ struct snd_pcm_ops {
 	snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
 	int (*wall_clock)(struct snd_pcm_substream *substream,
 			  struct timespec *audio_ts);
+	int (*start_at)(struct snd_pcm_substream *substream, int audio_tstamp_type, const struct timespec *ts);
+	int (*start_at_cancel)(struct snd_pcm_substream *substream);
 	int (*copy)(struct snd_pcm_substream *substream, int channel,
 		    snd_pcm_uframes_t pos,
 		    void __user *buf, snd_pcm_uframes_t count);
@@ -364,6 +366,10 @@ struct snd_pcm_runtime {
 #ifdef CONFIG_SND_PCM_XRUN_DEBUG
 	struct snd_pcm_hwptr_log *hwptr_log;
 #endif
+
+	int start_at_timer_running;		/* 0 if no timer is running, 1 if running */
+	int start_at_timer_tstamp_class;	/* tstamp class of current timer */
+	void* start_at_timer_data;		/* data associated with current timer */
 };
 
 struct snd_pcm_group {		/* keep linked substreams */
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 3/3] snd_pcm_start_at: Implement snd_pcm_start_at for system and audio tstamp types.
  2014-12-17 17:29 [PATCH v2 1/3] snd_pcm_start_at: Add enum for timestamp class, and helper struct for start_at ioctl Tim Cussins
  2014-12-17 17:29 ` [PATCH v2 2/3] snd_pcm_start_at: Add snd_pcm_ops so that driver can handle start_at requests Tim Cussins
@ 2014-12-17 17:29 ` Tim Cussins
  2014-12-17 23:37   ` Raymond Yau
  1 sibling, 1 reply; 5+ messages in thread
From: Tim Cussins @ 2014-12-17 17:29 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, nstoughton, Tim Cussins, pierre-louis.bossart

System tstamp types rely on high-res timers.
Audio tstamp types delegate to the pcm driver.
Current start_at timer is cancelled on attempt to change stream state.

Signed-off-by: Tim Cussins <timcussins@eml.cc>

diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 095d957..0ca2818 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -35,6 +35,9 @@
 #include <sound/timer.h>
 #include <sound/minors.h>
 #include <asm/io.h>
+#if defined(CONFIG_HIGH_RES_TIMERS)
+#include <linux/hrtimer.h>
+#endif
 
 /*
  *  Compatibility
@@ -67,6 +70,8 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
 #endif
 static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
 
+static int snd_pcm_start_at_cancel(struct snd_pcm_substream *substream);
+
 /*
  *
  */
@@ -834,6 +839,11 @@ static int snd_pcm_action_group(struct action_ops *ops,
 	struct snd_pcm_substream *s1;
 	int res = 0, depth = 1;
 
+	/* Any attempt to change state cancels a pending start_at timer */
+	res = snd_pcm_start_at_cancel(substream);
+	if (res < 0)
+		return res;
+
 	snd_pcm_group_for_each_entry(s, substream) {
 		if (do_lock && s != substream) {
 			if (s->pcm->nonatomic)
@@ -889,6 +899,11 @@ static int snd_pcm_action_single(struct action_ops *ops,
 {
 	int res;
 	
+	/* Any attempt to change state cancels a pending start_at timer */
+	res = snd_pcm_start_at_cancel(substream);
+	if (res < 0)
+		return res;
+
 	res = ops->pre_action(substream, state);
 	if (res < 0)
 		return res;
@@ -1015,6 +1030,191 @@ 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;
+
+   ret = snd_pcm_do_start(substream, SNDRV_PCM_STATE_RUNNING);
+   if (ret == 0) {
+       snd_pcm_post_start(substream, SNDRV_PCM_STATE_RUNNING);
+   }
+   return HRTIMER_NORESTART;
+}
+#endif
+
+static int snd_pcm_start_at_system(struct snd_pcm_substream *substream, int system_tstamp_type, const struct timespec *start_time)
+{
+#ifdef CONFIG_HIGH_RES_TIMERS
+	struct hrtimer_pcm *pcm_timer;
+	struct timespec now;
+	int ret;
+	clockid_t clock;
+
+	/* Get time now and check if start_time is in the past */
+	snd_pcm_gettime(substream->runtime, &now);
+	if (timespec_compare(&now, start_time) >= 0) {
+		return -ETIME;
+	}
+
+	switch (system_tstamp_type) {
+		case SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY:
+			clock = CLOCK_REALTIME;
+			break;
+		case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC:
+			clock = CLOCK_MONOTONIC;
+			break;
+		case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
+		default: /* unsupported clocks bounce off */
+			return -ENOSYS;
+	}
+
+	/* Allocate a hrtimer to handle the start_at */
+	pcm_timer = kmalloc(sizeof(*pcm_timer), GFP_KERNEL);
+	if (!pcm_timer)
+		return -ENOMEM;
+
+	hrtimer_init(&pcm_timer->timer, clock, HRTIMER_MODE_ABS);
+
+	/* Setup timer */
+	pcm_timer->timer.function = snd_pcm_do_start_time;
+	pcm_timer->substream = substream;
+
+	/* Store timer in runtime start_at info */
+	substream->runtime->start_at_timer_data = pcm_timer;
+
+	/* Pre start */
+	ret = snd_pcm_pre_start(substream, SNDRV_PCM_STATE_PREPARED);
+	if (ret < 0)
+		goto error;
+
+	ret = hrtimer_start(&pcm_timer->timer, timespec_to_ktime(*start_time), HRTIMER_MODE_ABS);
+	if (ret < 0 )
+		goto error;
+
+	return 0;
+error:
+	kfree(pcm_timer);
+	return ret;
+#else
+	return -ENOSYS;
+#endif
+}
+
+static int snd_pcm_start_at_system_cancel(struct snd_pcm_substream *substream)
+{
+#ifdef CONFIG_HIGH_RES_TIMERS
+	struct hrtimer_pcm *pcm_timer = substream->runtime->start_at_timer_data;
+	hrtimer_cancel(&pcm_timer->timer); /* Cancel existing timer. (NOP if it's not running) */
+	kfree(pcm_timer);
+	return 0;
+#else
+	return -ENOSYS;
+#endif
+}
+
+static int snd_pcm_start_at_audio(struct snd_pcm_substream *substream, int audio_tstamp_type, const struct timespec *start_time)
+{
+	if (substream->ops->start_at)
+		return substream->ops->start_at(substream, audio_tstamp_type, start_time);
+	else
+		return -ENOSYS;
+}
+
+static int snd_pcm_start_at_audio_cancel(struct snd_pcm_substream *substream)
+{
+	if (substream->ops->start_at_cancel)
+		return substream->ops->start_at_cancel(substream);
+	else
+		return -ENOSYS;
+}
+
+/* snd_pcm_start_at_cancel() allows state-transition code to conveniently cancel the pending timer */
+static int snd_pcm_start_at_cancel(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ret = 0;
+
+	if (runtime->start_at_timer_running) {
+		switch (runtime->start_at_timer_tstamp_class) {
+			case SNDRV_PCM_TSTAMP_CLASS_SYSTEM:
+				ret = snd_pcm_start_at_system_cancel(substream);
+				break;
+			case SNDRV_PCM_TSTAMP_CLASS_AUDIO:
+				ret = snd_pcm_start_at_audio_cancel(substream);
+				break;
+			default:
+				ret = -ENOSYS;
+		}
+	}
+
+	if (ret == 0) {
+		runtime->start_at_timer_running = 0;
+	}
+
+	return ret;
+}
+
+int snd_pcm_start_at(struct snd_pcm_substream *substream,
+        struct snd_start_at __user *_start_at)
+{
+	struct snd_start_at start_at;
+	int ret;
+
+	if (copy_from_user(&start_at, _start_at, sizeof(start_at)))
+		return -EFAULT;
+
+	if (!timespec_valid(&start_at.start_time))
+		return -EINVAL;
+
+	/*  If not a playback substream, give up */
+	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+		return -EINVAL;
+
+	/* Cancel any existing timer */
+	ret = snd_pcm_start_at_cancel(substream);
+	if (ret < 0)
+		return ret;
+
+	switch (start_at.tstamp_class) {
+		case SNDRV_PCM_TSTAMP_CLASS_SYSTEM:
+			ret = snd_pcm_start_at_system(substream, start_at.tstamp_type, &start_at.start_time);
+			break;
+		case SNDRV_PCM_TSTAMP_CLASS_AUDIO:
+			ret = snd_pcm_start_at_audio(substream, start_at.tstamp_type, &start_at.start_time);
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	if (ret == 0) {
+		substream->runtime->start_at_timer_tstamp_class = start_at.tstamp_class;
+		substream->runtime->start_at_timer_running = 1;
+	}
+
+	return ret;
+}
+
 /**
  * snd_pcm_start - start all linked streams
  * @substream: the PCM substream instance
@@ -2721,6 +2921,8 @@ static int snd_pcm_common_ioctl1(struct file *file,
 		return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
 	case SNDRV_PCM_IOCTL_LINK:
 		return snd_pcm_link(substream, (int)(unsigned long) arg);
+	case SNDRV_PCM_IOCTL_START_AT:
+		return snd_pcm_start_at(substream, arg);
 	case SNDRV_PCM_IOCTL_UNLINK:
 		return snd_pcm_unlink(substream);
 	case SNDRV_PCM_IOCTL_RESUME:
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 3/3] snd_pcm_start_at: Implement snd_pcm_start_at for system and audio tstamp types.
  2014-12-17 17:29 ` [PATCH v2 3/3] snd_pcm_start_at: Implement snd_pcm_start_at for system and audio tstamp types Tim Cussins
@ 2014-12-17 23:37   ` Raymond Yau
  2014-12-19 11:41     ` Tim Cussins
  0 siblings, 1 reply; 5+ messages in thread
From: Raymond Yau @ 2014-12-17 23:37 UTC (permalink / raw)
  To: Tim Cussins; +Cc: nstoughton, tiwai, alsa-devel, pierre-louis.bossart

>
> System tstamp types rely on high-res timers.
> Audio tstamp types delegate to the pcm driver.
> Current start_at timer is cancelled on attempt to change stream state.

http://git.alsa-project.org/?p=alsa-lib.git;a=blob_plain;f=test/timer.c;hb=HEAD

Are there any difference between sound card's timer with high res
timer/wall clock time stamp ?

It seem those sound cards timer have resolution and max tick

https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/commit/sound/pci/ymfpci?id=6e2efaacb3579fd9643d0dc59963b58b801c03a1

snd_timer_status_get_resolution

The clock tick counters are 32 bits and wrap around

This may mean that snd_pcm_start_at can only start within certian time
interval

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 3/3] snd_pcm_start_at: Implement snd_pcm_start_at for system and audio tstamp types.
  2014-12-17 23:37   ` Raymond Yau
@ 2014-12-19 11:41     ` Tim Cussins
  0 siblings, 0 replies; 5+ messages in thread
From: Tim Cussins @ 2014-12-19 11:41 UTC (permalink / raw)
  To: Raymond Yau; +Cc: nstoughton, tiwai, alsa-devel, pierre-louis.bossart

On 17/12/14 23:37, Raymond Yau wrote:
>  >
>  > System tstamp types rely on high-res timers.
>  > Audio tstamp types delegate to the pcm driver.
>  > Current start_at timer is cancelled on attempt to change stream state.
>
> http://git.alsa-project.org/?p=alsa-lib.git;a=blob_plain;f=test/timer.c;hb=HEAD
>
> Are there any difference between sound card's timer with high res
> timer/wall clock time stamp ?
>
> It seem those sound cards timer have resolution and max tick
>
> https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/commit/sound/pci/ymfpci?id=6e2efaacb3579fd9643d0dc59963b58b801c03a1
>
> snd_timer_status_get_resolution
>
> The clock tick counters are 32 bits and wrap around
>
> This may mean that snd_pcm_start_at can only start within certian time
> interval
>

[and this time I'll post to the list :)]

I had presumed it would be pretty simple to use the timer-wrap interrupt 
to maintain a monotonic sense of time. Hardware that can't support this 
might not be able to support start_at.

T

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-12-19 11:41 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-12-17 17:29 [PATCH v2 1/3] snd_pcm_start_at: Add enum for timestamp class, and helper struct for start_at ioctl Tim Cussins
2014-12-17 17:29 ` [PATCH v2 2/3] snd_pcm_start_at: Add snd_pcm_ops so that driver can handle start_at requests Tim Cussins
2014-12-17 17:29 ` [PATCH v2 3/3] snd_pcm_start_at: Implement snd_pcm_start_at for system and audio tstamp types Tim Cussins
2014-12-17 23:37   ` Raymond Yau
2014-12-19 11:41     ` Tim Cussins

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.