1 /* 2 * ALSA timer back-end using hrtimer 3 * Copyright (C) 2008 Takashi Iwai 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 * 19 */ 20 21 #include <linux/init.h> 22 #include <linux/module.h> 23 #include <linux/moduleparam.h> 24 #include <linux/hrtimer.h> 25 #include <sound/core.h> 26 #include <sound/timer.h> 27 28 MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 29 MODULE_DESCRIPTION("ALSA hrtimer backend"); 30 MODULE_LICENSE("GPL"); 31 32 MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_HRTIMER)); 33 34 #define NANO_SEC 1000000000UL /* 10^9 in sec */ 35 static unsigned int resolution; 36 37 struct snd_hrtimer { 38 struct snd_timer *timer; 39 struct hrtimer hrt; 40 }; 41 42 static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) 43 { 44 struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt); 45 struct snd_timer *t = stime->timer; 46 hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution)); 47 snd_timer_interrupt(stime->timer, t->sticks); 48 return HRTIMER_RESTART; 49 } 50 51 static int snd_hrtimer_open(struct snd_timer *t) 52 { 53 struct snd_hrtimer *stime; 54 55 stime = kmalloc(sizeof(*stime), GFP_KERNEL); 56 if (!stime) 57 return -ENOMEM; 58 hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 59 stime->timer = t; 60 stime->hrt.function = snd_hrtimer_callback; 61 t->private_data = stime; 62 return 0; 63 } 64 65 static int snd_hrtimer_close(struct snd_timer *t) 66 { 67 struct snd_hrtimer *stime = t->private_data; 68 69 if (stime) { 70 hrtimer_cancel(&stime->hrt); 71 kfree(stime); 72 t->private_data = NULL; 73 } 74 return 0; 75 } 76 77 static int snd_hrtimer_start(struct snd_timer *t) 78 { 79 struct snd_hrtimer *stime = t->private_data; 80 81 hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), 82 HRTIMER_MODE_REL); 83 return 0; 84 } 85 86 static int snd_hrtimer_stop(struct snd_timer *t) 87 { 88 struct snd_hrtimer *stime = t->private_data; 89 90 hrtimer_cancel(&stime->hrt); 91 return 0; 92 } 93 94 static struct snd_timer_hardware hrtimer_hw = { 95 .flags = SNDRV_TIMER_HW_AUTO, 96 .open = snd_hrtimer_open, 97 .close = snd_hrtimer_close, 98 .start = snd_hrtimer_start, 99 .stop = snd_hrtimer_stop, 100 }; 101 102 /* 103 * entry functions 104 */ 105 106 static struct snd_timer *mytimer; 107 108 static int __init snd_hrtimer_init(void) 109 { 110 struct snd_timer *timer; 111 struct timespec tp; 112 int err; 113 114 hrtimer_get_res(CLOCK_MONOTONIC, &tp); 115 if (tp.tv_sec > 0 || !tp.tv_nsec) { 116 snd_printk(KERN_ERR 117 "snd-hrtimer: Invalid resolution %u.%09u", 118 (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec); 119 return -EINVAL; 120 } 121 resolution = tp.tv_nsec; 122 123 /* Create a new timer and set up the fields */ 124 err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER, 125 &timer); 126 if (err < 0) 127 return err; 128 129 timer->module = THIS_MODULE; 130 strcpy(timer->name, "HR timer"); 131 timer->hw = hrtimer_hw; 132 timer->hw.resolution = resolution; 133 timer->hw.ticks = NANO_SEC / resolution; 134 135 err = snd_timer_global_register(timer); 136 if (err < 0) { 137 snd_timer_global_free(timer); 138 return err; 139 } 140 mytimer = timer; /* remember this */ 141 142 return 0; 143 } 144 145 static void __exit snd_hrtimer_exit(void) 146 { 147 if (mytimer) { 148 snd_timer_global_free(mytimer); 149 mytimer = NULL; 150 } 151 } 152 153 module_init(snd_hrtimer_init); 154 module_exit(snd_hrtimer_exit); 155