Commit ac5d1a7d253f3c02d1e5c93edfa26e81466ec71e
Committed by
Jaroslav Kysela
1 parent
2ea5814472
Exists in
master
and in
7 other branches
[ALSA] rtctimer: handle RTC interrupts with a tasklet
The calls to rtc_control() from inside the interrupt handler can upset the RTC code, so move our interrupt handling code to a tasklet. Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Showing 1 changed file with 14 additions and 6 deletions Side-by-side Diff
sound/core/rtctimer.c
... | ... | @@ -22,13 +22,10 @@ |
22 | 22 | |
23 | 23 | #include <sound/driver.h> |
24 | 24 | #include <linux/init.h> |
25 | -#include <linux/time.h> | |
26 | -#include <linux/threads.h> | |
27 | 25 | #include <linux/interrupt.h> |
28 | 26 | #include <linux/moduleparam.h> |
29 | 27 | #include <sound/core.h> |
30 | 28 | #include <sound/timer.h> |
31 | -#include <sound/info.h> | |
32 | 29 | |
33 | 30 | #if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE) |
34 | 31 | |
... | ... | @@ -50,7 +47,9 @@ |
50 | 47 | * The hardware dependent description for this timer. |
51 | 48 | */ |
52 | 49 | static struct snd_timer_hardware rtc_hw = { |
53 | - .flags = SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO, | |
50 | + .flags = SNDRV_TIMER_HW_AUTO | | |
51 | + SNDRV_TIMER_HW_FIRST | | |
52 | + SNDRV_TIMER_HW_TASKLET, | |
54 | 53 | .ticks = 100000000L, /* FIXME: XXX */ |
55 | 54 | .open = rtctimer_open, |
56 | 55 | .close = rtctimer_close, |
... | ... | @@ -60,6 +59,7 @@ |
60 | 59 | |
61 | 60 | static int rtctimer_freq = RTC_FREQ; /* frequency */ |
62 | 61 | static struct snd_timer *rtctimer; |
62 | +static struct tasklet_struct rtc_tasklet; | |
63 | 63 | static rtc_task_t rtc_task; |
64 | 64 | |
65 | 65 | |
... | ... | @@ -81,6 +81,7 @@ |
81 | 81 | rtc_task_t *rtc = t->private_data; |
82 | 82 | if (rtc) { |
83 | 83 | rtc_unregister(rtc); |
84 | + tasklet_kill(&rtc_tasklet); | |
84 | 85 | t->private_data = NULL; |
85 | 86 | } |
86 | 87 | return 0; |
87 | 88 | |
... | ... | @@ -105,12 +106,17 @@ |
105 | 106 | return 0; |
106 | 107 | } |
107 | 108 | |
109 | +static void rtctimer_tasklet(unsigned long data) | |
110 | +{ | |
111 | + snd_timer_interrupt((struct snd_timer *)data, 1); | |
112 | +} | |
113 | + | |
108 | 114 | /* |
109 | 115 | * interrupt |
110 | 116 | */ |
111 | 117 | static void rtctimer_interrupt(void *private_data) |
112 | 118 | { |
113 | - snd_timer_interrupt(private_data, 1); | |
119 | + tasklet_hi_schedule(private_data); | |
114 | 120 | } |
115 | 121 | |
116 | 122 | |
117 | 123 | |
... | ... | @@ -139,9 +145,11 @@ |
139 | 145 | timer->hw = rtc_hw; |
140 | 146 | timer->hw.resolution = NANO_SEC / rtctimer_freq; |
141 | 147 | |
148 | + tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer); | |
149 | + | |
142 | 150 | /* set up RTC callback */ |
143 | 151 | rtc_task.func = rtctimer_interrupt; |
144 | - rtc_task.private_data = timer; | |
152 | + rtc_task.private_data = &rtc_tasklet; | |
145 | 153 | |
146 | 154 | err = snd_timer_global_register(timer); |
147 | 155 | if (err < 0) { |