Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * ALSA timer back-end using hrtimer
0004  * Copyright (C) 2008 Takashi Iwai
0005  */
0006 
0007 #include <linux/init.h>
0008 #include <linux/slab.h>
0009 #include <linux/module.h>
0010 #include <linux/moduleparam.h>
0011 #include <linux/hrtimer.h>
0012 #include <sound/core.h>
0013 #include <sound/timer.h>
0014 
0015 MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
0016 MODULE_DESCRIPTION("ALSA hrtimer backend");
0017 MODULE_LICENSE("GPL");
0018 
0019 MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_HRTIMER));
0020 
0021 #define NANO_SEC    1000000000UL    /* 10^9 in sec */
0022 static unsigned int resolution;
0023 
0024 struct snd_hrtimer {
0025     struct snd_timer *timer;
0026     struct hrtimer hrt;
0027     bool in_callback;
0028 };
0029 
0030 static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
0031 {
0032     struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
0033     struct snd_timer *t = stime->timer;
0034     ktime_t delta;
0035     unsigned long ticks;
0036     enum hrtimer_restart ret = HRTIMER_NORESTART;
0037 
0038     spin_lock(&t->lock);
0039     if (!t->running)
0040         goto out; /* fast path */
0041     stime->in_callback = true;
0042     ticks = t->sticks;
0043     spin_unlock(&t->lock);
0044 
0045     /* calculate the drift */
0046     delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt));
0047     if (delta > 0)
0048         ticks += ktime_divns(delta, ticks * resolution);
0049 
0050     snd_timer_interrupt(stime->timer, ticks);
0051 
0052     spin_lock(&t->lock);
0053     if (t->running) {
0054         hrtimer_add_expires_ns(hrt, t->sticks * resolution);
0055         ret = HRTIMER_RESTART;
0056     }
0057 
0058     stime->in_callback = false;
0059  out:
0060     spin_unlock(&t->lock);
0061     return ret;
0062 }
0063 
0064 static int snd_hrtimer_open(struct snd_timer *t)
0065 {
0066     struct snd_hrtimer *stime;
0067 
0068     stime = kzalloc(sizeof(*stime), GFP_KERNEL);
0069     if (!stime)
0070         return -ENOMEM;
0071     hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
0072     stime->timer = t;
0073     stime->hrt.function = snd_hrtimer_callback;
0074     t->private_data = stime;
0075     return 0;
0076 }
0077 
0078 static int snd_hrtimer_close(struct snd_timer *t)
0079 {
0080     struct snd_hrtimer *stime = t->private_data;
0081 
0082     if (stime) {
0083         spin_lock_irq(&t->lock);
0084         t->running = 0; /* just to be sure */
0085         stime->in_callback = 1; /* skip start/stop */
0086         spin_unlock_irq(&t->lock);
0087 
0088         hrtimer_cancel(&stime->hrt);
0089         kfree(stime);
0090         t->private_data = NULL;
0091     }
0092     return 0;
0093 }
0094 
0095 static int snd_hrtimer_start(struct snd_timer *t)
0096 {
0097     struct snd_hrtimer *stime = t->private_data;
0098 
0099     if (stime->in_callback)
0100         return 0;
0101     hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
0102               HRTIMER_MODE_REL);
0103     return 0;
0104 }
0105 
0106 static int snd_hrtimer_stop(struct snd_timer *t)
0107 {
0108     struct snd_hrtimer *stime = t->private_data;
0109 
0110     if (stime->in_callback)
0111         return 0;
0112     hrtimer_try_to_cancel(&stime->hrt);
0113     return 0;
0114 }
0115 
0116 static const struct snd_timer_hardware hrtimer_hw __initconst = {
0117     .flags =    SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK,
0118     .open =     snd_hrtimer_open,
0119     .close =    snd_hrtimer_close,
0120     .start =    snd_hrtimer_start,
0121     .stop =     snd_hrtimer_stop,
0122 };
0123 
0124 /*
0125  * entry functions
0126  */
0127 
0128 static struct snd_timer *mytimer;
0129 
0130 static int __init snd_hrtimer_init(void)
0131 {
0132     struct snd_timer *timer;
0133     int err;
0134 
0135     resolution = hrtimer_resolution;
0136 
0137     /* Create a new timer and set up the fields */
0138     err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER,
0139                    &timer);
0140     if (err < 0)
0141         return err;
0142 
0143     timer->module = THIS_MODULE;
0144     strcpy(timer->name, "HR timer");
0145     timer->hw = hrtimer_hw;
0146     timer->hw.resolution = resolution;
0147     timer->hw.ticks = NANO_SEC / resolution;
0148     timer->max_instances = 100; /* lower the limit */
0149 
0150     err = snd_timer_global_register(timer);
0151     if (err < 0) {
0152         snd_timer_global_free(timer);
0153         return err;
0154     }
0155     mytimer = timer; /* remember this */
0156 
0157     return 0;
0158 }
0159 
0160 static void __exit snd_hrtimer_exit(void)
0161 {
0162     if (mytimer) {
0163         snd_timer_global_free(mytimer);
0164         mytimer = NULL;
0165     }
0166 }
0167 
0168 module_init(snd_hrtimer_init);
0169 module_exit(snd_hrtimer_exit);