Loading Documentation/sound/alsa/timestamping.txt +6 −6 Original line number Original line Diff line number Diff line Loading @@ -14,7 +14,7 @@ provides a refined estimate with a delay. event or application query. event or application query. The difference (tstamp - trigger_tstamp) defines the elapsed time. The difference (tstamp - trigger_tstamp) defines the elapsed time. The ALSA API provides reports two basic pieces of information, avail The ALSA API provides two basic pieces of information, avail and delay, which combined with the trigger and current system and delay, which combined with the trigger and current system timestamps allow for applications to keep track of the 'fullness' of timestamps allow for applications to keep track of the 'fullness' of the ring buffer and the amount of queued samples. the ring buffer and the amount of queued samples. Loading Loading @@ -53,21 +53,21 @@ case): The analog time is taken at the last stage of the playback, as close The analog time is taken at the last stage of the playback, as close as possible to the actual transducer as possible to the actual transducer The link time is taken at the output of the SOC/chipset as the samples The link time is taken at the output of the SoC/chipset as the samples are pushed on a link. The link time can be directly measured if are pushed on a link. The link time can be directly measured if supported in hardware by sample counters or wallclocks (e.g. with supported in hardware by sample counters or wallclocks (e.g. with HDAudio 24MHz or PTP clock for networked solutions) or indirectly HDAudio 24MHz or PTP clock for networked solutions) or indirectly estimated (e.g. with the frame counter in USB). estimated (e.g. with the frame counter in USB). The DMA time is measured using counters - typically the least reliable The DMA time is measured using counters - typically the least reliable of all measurements due to the bursty natured of DMA transfers. of all measurements due to the bursty nature of DMA transfers. The app time corresponds to the time tracked by an application after The app time corresponds to the time tracked by an application after writing in the ring buffer. writing in the ring buffer. The application can query what the hardware supports, define which The application can query the hardware capabilities, define which audio time it wants reported by selecting the relevant settings in audio time it wants reported by selecting the relevant settings in audio_tstamp_config fields, get an estimate of the timestamp audio_tstamp_config fields, thus get an estimate of the timestamp accuracy. It can also request the delay-to-analog be included in the accuracy. It can also request the delay-to-analog be included in the measurement. Direct access to the link time is very interesting on measurement. Direct access to the link time is very interesting on platforms that provide an embedded DSP; measuring directly the link platforms that provide an embedded DSP; measuring directly the link Loading Loading @@ -169,7 +169,7 @@ playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -51 Example 1 shows that the timestamp at the DMA level is close to 1ms Example 1 shows that the timestamp at the DMA level is close to 1ms ahead of the actual playback time (as a side time this sort of ahead of the actual playback time (as a side time this sort of measurement can help define rewind safeguards). Compensating for the measurement can help define rewind safeguards). Compensating for the DMA-link delay in example 2 helps remove the hardware buffering abut DMA-link delay in example 2 helps remove the hardware buffering but the information is still very jittery, with up to one sample of the information is still very jittery, with up to one sample of error. In example 3 where the timestamps are measured with the link error. In example 3 where the timestamps are measured with the link wallclock, the timestamps show a monotonic behavior and a lower wallclock, the timestamps show a monotonic behavior and a lower Loading sound/core/control.c +32 −0 Original line number Original line Diff line number Diff line Loading @@ -807,6 +807,36 @@ static int snd_ctl_elem_list(struct snd_card *card, return 0; return 0; } } static bool validate_element_member_dimension(struct snd_ctl_elem_info *info) { unsigned int members; unsigned int i; if (info->dimen.d[0] == 0) return true; members = 1; for (i = 0; i < ARRAY_SIZE(info->dimen.d); ++i) { if (info->dimen.d[i] == 0) break; members *= info->dimen.d[i]; /* * info->count should be validated in advance, to guarantee * calculation soundness. */ if (members > info->count) return false; } for (++i; i < ARRAY_SIZE(info->dimen.d); ++i) { if (info->dimen.d[i] > 0) return false; } return members == info->count; } static int snd_ctl_elem_info(struct snd_ctl_file *ctl, static int snd_ctl_elem_info(struct snd_ctl_file *ctl, struct snd_ctl_elem_info *info) struct snd_ctl_elem_info *info) { { Loading Loading @@ -1274,6 +1304,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (info->count < 1 || if (info->count < 1 || info->count > max_value_counts[info->type]) info->count > max_value_counts[info->type]) return -EINVAL; return -EINVAL; if (!validate_element_member_dimension(info)) return -EINVAL; private_size = value_sizes[info->type] * info->count; private_size = value_sizes[info->type] * info->count; /* /* Loading sound/core/seq/oss/seq_oss_synth.c +5 −5 Original line number Original line Diff line number Diff line Loading @@ -70,11 +70,11 @@ struct seq_oss_synth { static int max_synth_devs; static int max_synth_devs; static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; static struct seq_oss_synth midi_synth_dev = { static struct seq_oss_synth midi_synth_dev = { -1, /* seq_device */ .seq_device = -1, SYNTH_TYPE_MIDI, /* synth_type */ .synth_type = SYNTH_TYPE_MIDI, 0, /* synth_subtype */ .synth_subtype = 0, 16, /* nr_voices */ .nr_voices = 16, "MIDI", /* name */ .name = "MIDI", }; }; static DEFINE_SPINLOCK(register_lock); static DEFINE_SPINLOCK(register_lock); Loading sound/core/seq/seq_timer.c +9 −14 Original line number Original line Diff line number Diff line Loading @@ -165,7 +165,7 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, snd_seq_timer_update_tick(&tmr->tick, resolution); snd_seq_timer_update_tick(&tmr->tick, resolution); /* register actual time of this timer update */ /* register actual time of this timer update */ do_gettimeofday(&tmr->last_update); ktime_get_ts64(&tmr->last_update); spin_unlock_irqrestore(&tmr->lock, flags); spin_unlock_irqrestore(&tmr->lock, flags); Loading Loading @@ -392,7 +392,7 @@ static int seq_timer_start(struct snd_seq_timer *tmr) return -EINVAL; return -EINVAL; snd_timer_start(tmr->timeri, tmr->ticks); snd_timer_start(tmr->timeri, tmr->ticks); tmr->running = 1; tmr->running = 1; do_gettimeofday(&tmr->last_update); ktime_get_ts64(&tmr->last_update); return 0; return 0; } } Loading Loading @@ -420,7 +420,7 @@ static int seq_timer_continue(struct snd_seq_timer *tmr) } } snd_timer_start(tmr->timeri, tmr->ticks); snd_timer_start(tmr->timeri, tmr->ticks); tmr->running = 1; tmr->running = 1; do_gettimeofday(&tmr->last_update); ktime_get_ts64(&tmr->last_update); return 0; return 0; } } Loading @@ -444,17 +444,12 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) spin_lock_irqsave(&tmr->lock, flags); spin_lock_irqsave(&tmr->lock, flags); cur_time = tmr->cur_time; cur_time = tmr->cur_time; if (tmr->running) { if (tmr->running) { struct timeval tm; struct timespec64 tm; int usec; do_gettimeofday(&tm); ktime_get_ts64(&tm); usec = (int)(tm.tv_usec - tmr->last_update.tv_usec); tm = timespec64_sub(tm, tmr->last_update); if (usec < 0) { cur_time.tv_nsec = tm.tv_nsec; cur_time.tv_nsec += (1000000 + usec) * 1000; cur_time.tv_sec = tm.tv_sec; cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1; } else { cur_time.tv_nsec += usec * 1000; cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec; } snd_seq_sanity_real_time(&cur_time); snd_seq_sanity_real_time(&cur_time); } } spin_unlock_irqrestore(&tmr->lock, flags); spin_unlock_irqrestore(&tmr->lock, flags); Loading sound/core/seq/seq_timer.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -52,7 +52,7 @@ struct snd_seq_timer { unsigned int skew; unsigned int skew; unsigned int skew_base; unsigned int skew_base; struct timeval last_update; /* time of last clock update, used for interpolation */ struct timespec64 last_update; /* time of last clock update, used for interpolation */ spinlock_t lock; spinlock_t lock; }; }; Loading Loading
Documentation/sound/alsa/timestamping.txt +6 −6 Original line number Original line Diff line number Diff line Loading @@ -14,7 +14,7 @@ provides a refined estimate with a delay. event or application query. event or application query. The difference (tstamp - trigger_tstamp) defines the elapsed time. The difference (tstamp - trigger_tstamp) defines the elapsed time. The ALSA API provides reports two basic pieces of information, avail The ALSA API provides two basic pieces of information, avail and delay, which combined with the trigger and current system and delay, which combined with the trigger and current system timestamps allow for applications to keep track of the 'fullness' of timestamps allow for applications to keep track of the 'fullness' of the ring buffer and the amount of queued samples. the ring buffer and the amount of queued samples. Loading Loading @@ -53,21 +53,21 @@ case): The analog time is taken at the last stage of the playback, as close The analog time is taken at the last stage of the playback, as close as possible to the actual transducer as possible to the actual transducer The link time is taken at the output of the SOC/chipset as the samples The link time is taken at the output of the SoC/chipset as the samples are pushed on a link. The link time can be directly measured if are pushed on a link. The link time can be directly measured if supported in hardware by sample counters or wallclocks (e.g. with supported in hardware by sample counters or wallclocks (e.g. with HDAudio 24MHz or PTP clock for networked solutions) or indirectly HDAudio 24MHz or PTP clock for networked solutions) or indirectly estimated (e.g. with the frame counter in USB). estimated (e.g. with the frame counter in USB). The DMA time is measured using counters - typically the least reliable The DMA time is measured using counters - typically the least reliable of all measurements due to the bursty natured of DMA transfers. of all measurements due to the bursty nature of DMA transfers. The app time corresponds to the time tracked by an application after The app time corresponds to the time tracked by an application after writing in the ring buffer. writing in the ring buffer. The application can query what the hardware supports, define which The application can query the hardware capabilities, define which audio time it wants reported by selecting the relevant settings in audio time it wants reported by selecting the relevant settings in audio_tstamp_config fields, get an estimate of the timestamp audio_tstamp_config fields, thus get an estimate of the timestamp accuracy. It can also request the delay-to-analog be included in the accuracy. It can also request the delay-to-analog be included in the measurement. Direct access to the link time is very interesting on measurement. Direct access to the link time is very interesting on platforms that provide an embedded DSP; measuring directly the link platforms that provide an embedded DSP; measuring directly the link Loading Loading @@ -169,7 +169,7 @@ playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -51 Example 1 shows that the timestamp at the DMA level is close to 1ms Example 1 shows that the timestamp at the DMA level is close to 1ms ahead of the actual playback time (as a side time this sort of ahead of the actual playback time (as a side time this sort of measurement can help define rewind safeguards). Compensating for the measurement can help define rewind safeguards). Compensating for the DMA-link delay in example 2 helps remove the hardware buffering abut DMA-link delay in example 2 helps remove the hardware buffering but the information is still very jittery, with up to one sample of the information is still very jittery, with up to one sample of error. In example 3 where the timestamps are measured with the link error. In example 3 where the timestamps are measured with the link wallclock, the timestamps show a monotonic behavior and a lower wallclock, the timestamps show a monotonic behavior and a lower Loading
sound/core/control.c +32 −0 Original line number Original line Diff line number Diff line Loading @@ -807,6 +807,36 @@ static int snd_ctl_elem_list(struct snd_card *card, return 0; return 0; } } static bool validate_element_member_dimension(struct snd_ctl_elem_info *info) { unsigned int members; unsigned int i; if (info->dimen.d[0] == 0) return true; members = 1; for (i = 0; i < ARRAY_SIZE(info->dimen.d); ++i) { if (info->dimen.d[i] == 0) break; members *= info->dimen.d[i]; /* * info->count should be validated in advance, to guarantee * calculation soundness. */ if (members > info->count) return false; } for (++i; i < ARRAY_SIZE(info->dimen.d); ++i) { if (info->dimen.d[i] > 0) return false; } return members == info->count; } static int snd_ctl_elem_info(struct snd_ctl_file *ctl, static int snd_ctl_elem_info(struct snd_ctl_file *ctl, struct snd_ctl_elem_info *info) struct snd_ctl_elem_info *info) { { Loading Loading @@ -1274,6 +1304,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (info->count < 1 || if (info->count < 1 || info->count > max_value_counts[info->type]) info->count > max_value_counts[info->type]) return -EINVAL; return -EINVAL; if (!validate_element_member_dimension(info)) return -EINVAL; private_size = value_sizes[info->type] * info->count; private_size = value_sizes[info->type] * info->count; /* /* Loading
sound/core/seq/oss/seq_oss_synth.c +5 −5 Original line number Original line Diff line number Diff line Loading @@ -70,11 +70,11 @@ struct seq_oss_synth { static int max_synth_devs; static int max_synth_devs; static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; static struct seq_oss_synth midi_synth_dev = { static struct seq_oss_synth midi_synth_dev = { -1, /* seq_device */ .seq_device = -1, SYNTH_TYPE_MIDI, /* synth_type */ .synth_type = SYNTH_TYPE_MIDI, 0, /* synth_subtype */ .synth_subtype = 0, 16, /* nr_voices */ .nr_voices = 16, "MIDI", /* name */ .name = "MIDI", }; }; static DEFINE_SPINLOCK(register_lock); static DEFINE_SPINLOCK(register_lock); Loading
sound/core/seq/seq_timer.c +9 −14 Original line number Original line Diff line number Diff line Loading @@ -165,7 +165,7 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, snd_seq_timer_update_tick(&tmr->tick, resolution); snd_seq_timer_update_tick(&tmr->tick, resolution); /* register actual time of this timer update */ /* register actual time of this timer update */ do_gettimeofday(&tmr->last_update); ktime_get_ts64(&tmr->last_update); spin_unlock_irqrestore(&tmr->lock, flags); spin_unlock_irqrestore(&tmr->lock, flags); Loading Loading @@ -392,7 +392,7 @@ static int seq_timer_start(struct snd_seq_timer *tmr) return -EINVAL; return -EINVAL; snd_timer_start(tmr->timeri, tmr->ticks); snd_timer_start(tmr->timeri, tmr->ticks); tmr->running = 1; tmr->running = 1; do_gettimeofday(&tmr->last_update); ktime_get_ts64(&tmr->last_update); return 0; return 0; } } Loading Loading @@ -420,7 +420,7 @@ static int seq_timer_continue(struct snd_seq_timer *tmr) } } snd_timer_start(tmr->timeri, tmr->ticks); snd_timer_start(tmr->timeri, tmr->ticks); tmr->running = 1; tmr->running = 1; do_gettimeofday(&tmr->last_update); ktime_get_ts64(&tmr->last_update); return 0; return 0; } } Loading @@ -444,17 +444,12 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) spin_lock_irqsave(&tmr->lock, flags); spin_lock_irqsave(&tmr->lock, flags); cur_time = tmr->cur_time; cur_time = tmr->cur_time; if (tmr->running) { if (tmr->running) { struct timeval tm; struct timespec64 tm; int usec; do_gettimeofday(&tm); ktime_get_ts64(&tm); usec = (int)(tm.tv_usec - tmr->last_update.tv_usec); tm = timespec64_sub(tm, tmr->last_update); if (usec < 0) { cur_time.tv_nsec = tm.tv_nsec; cur_time.tv_nsec += (1000000 + usec) * 1000; cur_time.tv_sec = tm.tv_sec; cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1; } else { cur_time.tv_nsec += usec * 1000; cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec; } snd_seq_sanity_real_time(&cur_time); snd_seq_sanity_real_time(&cur_time); } } spin_unlock_irqrestore(&tmr->lock, flags); spin_unlock_irqrestore(&tmr->lock, flags); Loading
sound/core/seq/seq_timer.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -52,7 +52,7 @@ struct snd_seq_timer { unsigned int skew; unsigned int skew; unsigned int skew_base; unsigned int skew_base; struct timeval last_update; /* time of last clock update, used for interpolation */ struct timespec64 last_update; /* time of last clock update, used for interpolation */ spinlock_t lock; spinlock_t lock; }; }; Loading