diff options
author | Jaroslav Kysela <perex@perex.cz> | 2007-12-13 04:19:42 -0500 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2008-01-31 11:29:31 -0500 |
commit | b751eef1fdffca5532344285f2fad0c60d2f0158 (patch) | |
tree | d504104c6315a8abc4b3f657f8f4828fb55a8795 | |
parent | 25543fa785a32ce22e7374ba403eb6d38854d037 (diff) |
[ALSA] Use posix clock monotonic for PCM and timer timestamps
We need an accurate and continuous (monotonic) time sources to do
accurate synchronization among more timing sources. This patch allows
to enable monotonic timestamps for ALSA PCM devices and enables monotonic
timestamps for ALSA timer devices.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r-- | include/sound/asound.h | 10 | ||||
-rw-r--r-- | include/sound/pcm.h | 10 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 2 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 25 | ||||
-rw-r--r-- | sound/core/timer.c | 16 |
5 files changed, 52 insertions, 11 deletions
diff --git a/include/sound/asound.h b/include/sound/asound.h index 3ad534149c06..475eb71d65ba 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h | |||
@@ -138,7 +138,7 @@ enum { | |||
138 | * * | 138 | * * |
139 | *****************************************************************************/ | 139 | *****************************************************************************/ |
140 | 140 | ||
141 | #define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 8) | 141 | #define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 9) |
142 | 142 | ||
143 | typedef unsigned long snd_pcm_uframes_t; | 143 | typedef unsigned long snd_pcm_uframes_t; |
144 | typedef signed long snd_pcm_sframes_t; | 144 | typedef signed long snd_pcm_sframes_t; |
@@ -435,9 +435,15 @@ struct snd_xfern { | |||
435 | }; | 435 | }; |
436 | 436 | ||
437 | enum { | 437 | enum { |
438 | SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0, /* gettimeofday equivalent */ | ||
439 | SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, /* posix_clock_monotonic equivalent */ | ||
440 | SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, | ||
441 | }; | ||
442 | |||
443 | enum { | ||
438 | SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int), | 444 | SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int), |
439 | SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct snd_pcm_info), | 445 | SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct snd_pcm_info), |
440 | SNDRV_PCM_IOCTL_TSTAMP = _IOW('A', 0x02, int), | 446 | SNDRV_PCM_IOCTL_TTSTAMP = _IOW('A', 0x03, int), |
441 | SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct snd_pcm_hw_params), | 447 | SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct snd_pcm_hw_params), |
442 | SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct snd_pcm_hw_params), | 448 | SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct snd_pcm_hw_params), |
443 | SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12), | 449 | SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12), |
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 5e9cc460075e..65f636223d39 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h | |||
@@ -323,6 +323,7 @@ struct snd_pcm_runtime { | |||
323 | 323 | ||
324 | /* -- timer -- */ | 324 | /* -- timer -- */ |
325 | unsigned int timer_resolution; /* timer resolution */ | 325 | unsigned int timer_resolution; /* timer resolution */ |
326 | int tstamp_type; /* timestamp type */ | ||
326 | 327 | ||
327 | /* -- DMA -- */ | 328 | /* -- DMA -- */ |
328 | unsigned char *dma_area; /* DMA area */ | 329 | unsigned char *dma_area; /* DMA area */ |
@@ -952,6 +953,15 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream); | |||
952 | void snd_pcm_timer_init(struct snd_pcm_substream *substream); | 953 | void snd_pcm_timer_init(struct snd_pcm_substream *substream); |
953 | void snd_pcm_timer_done(struct snd_pcm_substream *substream); | 954 | void snd_pcm_timer_done(struct snd_pcm_substream *substream); |
954 | 955 | ||
956 | static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime, | ||
957 | struct timespec *tv) | ||
958 | { | ||
959 | if (runtime->tstamp_type == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC) | ||
960 | do_posix_clock_monotonic_gettime(tv); | ||
961 | else | ||
962 | getnstimeofday(tv); | ||
963 | } | ||
964 | |||
955 | /* | 965 | /* |
956 | * Memory | 966 | * Memory |
957 | */ | 967 | */ |
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 93d7ca502730..db3d7e934ec3 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -188,7 +188,7 @@ static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *subs | |||
188 | snd_pcm_sframes_t delta; | 188 | snd_pcm_sframes_t delta; |
189 | 189 | ||
190 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_MMAP) | 190 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_MMAP) |
191 | getnstimeofday((struct timespec *)&runtime->status->tstamp); | 191 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); |
192 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); | 192 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); |
193 | if (pos == SNDRV_PCM_POS_XRUN) { | 193 | if (pos == SNDRV_PCM_POS_XRUN) { |
194 | xrun(substream); | 194 | xrun(substream); |
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 6245bdaffa68..cdeae7c46e3b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c | |||
@@ -598,9 +598,9 @@ int snd_pcm_status(struct snd_pcm_substream *substream, | |||
598 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_MMAP) | 598 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_MMAP) |
599 | status->tstamp = runtime->status->tstamp; | 599 | status->tstamp = runtime->status->tstamp; |
600 | else | 600 | else |
601 | getnstimeofday(&status->tstamp); | 601 | snd_pcm_gettime(runtime, &status->tstamp); |
602 | } else | 602 | } else |
603 | getnstimeofday(&status->tstamp); | 603 | snd_pcm_gettime(runtime, &status->tstamp); |
604 | status->appl_ptr = runtime->control->appl_ptr; | 604 | status->appl_ptr = runtime->control->appl_ptr; |
605 | status->hw_ptr = runtime->status->hw_ptr; | 605 | status->hw_ptr = runtime->status->hw_ptr; |
606 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 606 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
@@ -688,7 +688,7 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream) | |||
688 | if (runtime->trigger_master == NULL) | 688 | if (runtime->trigger_master == NULL) |
689 | return; | 689 | return; |
690 | if (runtime->trigger_master == substream) { | 690 | if (runtime->trigger_master == substream) { |
691 | getnstimeofday(&runtime->trigger_tstamp); | 691 | snd_pcm_gettime(runtime, &runtime->trigger_tstamp); |
692 | } else { | 692 | } else { |
693 | snd_pcm_trigger_tstamp(runtime->trigger_master); | 693 | snd_pcm_trigger_tstamp(runtime->trigger_master); |
694 | runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; | 694 | runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; |
@@ -2519,6 +2519,21 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, | |||
2519 | return -EFAULT; | 2519 | return -EFAULT; |
2520 | return 0; | 2520 | return 0; |
2521 | } | 2521 | } |
2522 | |||
2523 | static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg) | ||
2524 | { | ||
2525 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
2526 | int arg; | ||
2527 | |||
2528 | if (get_user(arg, _arg)) | ||
2529 | return -EFAULT; | ||
2530 | if (arg < 0 || arg > SNDRV_PCM_TSTAMP_TYPE_LAST) | ||
2531 | return -EINVAL; | ||
2532 | runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY; | ||
2533 | if (arg == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC) | ||
2534 | runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; | ||
2535 | return 0; | ||
2536 | } | ||
2522 | 2537 | ||
2523 | static int snd_pcm_common_ioctl1(struct file *file, | 2538 | static int snd_pcm_common_ioctl1(struct file *file, |
2524 | struct snd_pcm_substream *substream, | 2539 | struct snd_pcm_substream *substream, |
@@ -2531,8 +2546,8 @@ static int snd_pcm_common_ioctl1(struct file *file, | |||
2531 | return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; | 2546 | return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; |
2532 | case SNDRV_PCM_IOCTL_INFO: | 2547 | case SNDRV_PCM_IOCTL_INFO: |
2533 | return snd_pcm_info_user(substream, arg); | 2548 | return snd_pcm_info_user(substream, arg); |
2534 | case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */ | 2549 | case SNDRV_PCM_IOCTL_TTSTAMP: |
2535 | return 0; | 2550 | return snd_pcm_tstamp(substream, arg); |
2536 | case SNDRV_PCM_IOCTL_HW_REFINE: | 2551 | case SNDRV_PCM_IOCTL_HW_REFINE: |
2537 | return snd_pcm_hw_refine_user(substream, arg); | 2552 | return snd_pcm_hw_refine_user(substream, arg); |
2538 | case SNDRV_PCM_IOCTL_HW_PARAMS: | 2553 | case SNDRV_PCM_IOCTL_HW_PARAMS: |
diff --git a/sound/core/timer.c b/sound/core/timer.c index e7dc56ca4b97..7e5fe2d91662 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c | |||
@@ -44,11 +44,14 @@ | |||
44 | #endif | 44 | #endif |
45 | 45 | ||
46 | static int timer_limit = DEFAULT_TIMER_LIMIT; | 46 | static int timer_limit = DEFAULT_TIMER_LIMIT; |
47 | static int timer_tstamp_monotonic = 1; | ||
47 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>"); | 48 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>"); |
48 | MODULE_DESCRIPTION("ALSA timer interface"); | 49 | MODULE_DESCRIPTION("ALSA timer interface"); |
49 | MODULE_LICENSE("GPL"); | 50 | MODULE_LICENSE("GPL"); |
50 | module_param(timer_limit, int, 0444); | 51 | module_param(timer_limit, int, 0444); |
51 | MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); | 52 | MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); |
53 | module_param(timer_tstamp_monotonic, int, 0444); | ||
54 | MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default)."); | ||
52 | 55 | ||
53 | struct snd_timer_user { | 56 | struct snd_timer_user { |
54 | struct snd_timer_instance *timeri; | 57 | struct snd_timer_instance *timeri; |
@@ -381,7 +384,10 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) | |||
381 | struct snd_timer_instance *ts; | 384 | struct snd_timer_instance *ts; |
382 | struct timespec tstamp; | 385 | struct timespec tstamp; |
383 | 386 | ||
384 | getnstimeofday(&tstamp); | 387 | if (timer_tstamp_monotonic) |
388 | do_posix_clock_monotonic_gettime(&tstamp); | ||
389 | else | ||
390 | getnstimeofday(&tstamp); | ||
385 | snd_assert(event >= SNDRV_TIMER_EVENT_START && | 391 | snd_assert(event >= SNDRV_TIMER_EVENT_START && |
386 | event <= SNDRV_TIMER_EVENT_PAUSE, return); | 392 | event <= SNDRV_TIMER_EVENT_PAUSE, return); |
387 | if (event == SNDRV_TIMER_EVENT_START || | 393 | if (event == SNDRV_TIMER_EVENT_START || |
@@ -1182,8 +1188,12 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, | |||
1182 | spin_unlock(&tu->qlock); | 1188 | spin_unlock(&tu->qlock); |
1183 | return; | 1189 | return; |
1184 | } | 1190 | } |
1185 | if (tu->last_resolution != resolution || ticks > 0) | 1191 | if (tu->last_resolution != resolution || ticks > 0) { |
1186 | getnstimeofday(&tstamp); | 1192 | if (timer_tstamp_monotonic) |
1193 | do_posix_clock_monotonic_gettime(&tstamp); | ||
1194 | else | ||
1195 | getnstimeofday(&tstamp); | ||
1196 | } | ||
1187 | if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && | 1197 | if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && |
1188 | tu->last_resolution != resolution) { | 1198 | tu->last_resolution != resolution) { |
1189 | r1.event = SNDRV_TIMER_EVENT_RESOLUTION; | 1199 | r1.event = SNDRV_TIMER_EVENT_RESOLUTION; |