diff options
Diffstat (limited to 'sound/core/hrtimer.c')
| -rw-r--r-- | sound/core/hrtimer.c | 88 |
1 files changed, 44 insertions, 44 deletions
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index b8b31c433d64..2d5f4d47071f 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -1,25 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * ALSA timer back-end using hrtimer * Copyright (C) 2008 Takashi Iwai - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include <linux/init.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/hrtimer.h> @@ -38,37 +25,50 @@ static unsigned int resolution; struct snd_hrtimer { struct snd_timer *timer; struct hrtimer hrt; - atomic_t running; + bool in_callback; }; static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) { struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt); struct snd_timer *t = stime->timer; - unsigned long oruns; + ktime_t delta; + unsigned long ticks; + enum hrtimer_restart ret = HRTIMER_NORESTART; + + scoped_guard(spinlock, &t->lock) { + if (!t->running) + return HRTIMER_NORESTART; /* fast path */ + stime->in_callback = true; + ticks = t->sticks; + } + + /* calculate the drift */ + delta = ktime_sub(hrtimer_cb_get_time(hrt), hrtimer_get_expires(hrt)); + if (delta > 0) + ticks += ktime_divns(delta, ticks * resolution); - if (!atomic_read(&stime->running)) - return HRTIMER_NORESTART; + snd_timer_interrupt(stime->timer, ticks); - oruns = hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution)); - snd_timer_interrupt(stime->timer, t->sticks * oruns); + guard(spinlock)(&t->lock); + if (t->running) { + hrtimer_add_expires_ns(hrt, t->sticks * resolution); + ret = HRTIMER_RESTART; + } - if (!atomic_read(&stime->running)) - return HRTIMER_NORESTART; - return HRTIMER_RESTART; + stime->in_callback = false; + return ret; } static int snd_hrtimer_open(struct snd_timer *t) { struct snd_hrtimer *stime; - stime = kmalloc(sizeof(*stime), GFP_KERNEL); + stime = kzalloc(sizeof(*stime), GFP_KERNEL); if (!stime) return -ENOMEM; - hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); stime->timer = t; - stime->hrt.function = snd_hrtimer_callback; - atomic_set(&stime->running, 0); + hrtimer_setup(&stime->hrt, snd_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL); t->private_data = stime; return 0; } @@ -78,6 +78,11 @@ static int snd_hrtimer_close(struct snd_timer *t) struct snd_hrtimer *stime = t->private_data; if (stime) { + scoped_guard(spinlock_irq, &t->lock) { + t->running = 0; /* just to be sure */ + stime->in_callback = 1; /* skip start/stop */ + } + hrtimer_cancel(&stime->hrt); kfree(stime); t->private_data = NULL; @@ -89,23 +94,25 @@ static int snd_hrtimer_start(struct snd_timer *t) { struct snd_hrtimer *stime = t->private_data; - atomic_set(&stime->running, 0); - hrtimer_cancel(&stime->hrt); + if (stime->in_callback) + return 0; hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), HRTIMER_MODE_REL); - atomic_set(&stime->running, 1); return 0; } static int snd_hrtimer_stop(struct snd_timer *t) { struct snd_hrtimer *stime = t->private_data; - atomic_set(&stime->running, 0); + + if (stime->in_callback) + return 0; + hrtimer_try_to_cancel(&stime->hrt); return 0; } -static struct snd_timer_hardware hrtimer_hw = { - .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET, +static const struct snd_timer_hardware hrtimer_hw __initconst = { + .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK, .open = snd_hrtimer_open, .close = snd_hrtimer_close, .start = snd_hrtimer_start, @@ -121,17 +128,9 @@ static struct snd_timer *mytimer; static int __init snd_hrtimer_init(void) { struct snd_timer *timer; - struct timespec tp; int err; - hrtimer_get_res(CLOCK_MONOTONIC, &tp); - if (tp.tv_sec > 0 || !tp.tv_nsec) { - snd_printk(KERN_ERR - "snd-hrtimer: Invalid resolution %u.%09u", - (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec); - return -EINVAL; - } - resolution = tp.tv_nsec; + resolution = hrtimer_resolution; /* Create a new timer and set up the fields */ err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER, @@ -140,10 +139,11 @@ static int __init snd_hrtimer_init(void) return err; timer->module = THIS_MODULE; - strcpy(timer->name, "HR timer"); + strscpy(timer->name, "HR timer"); timer->hw = hrtimer_hw; timer->hw.resolution = resolution; timer->hw.ticks = NANO_SEC / resolution; + timer->max_instances = 100; /* lower the limit */ err = snd_timer_global_register(timer); if (err < 0) { |
