diff options
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/seq/seq.c | 3 | ||||
-rw-r--r-- | sound/core/seq/seq_device.c | 92 |
2 files changed, 72 insertions, 23 deletions
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c index 712110561082..bebdd2e920ca 100644 --- a/sound/core/seq/seq.c +++ b/sound/core/seq/seq.c | |||
@@ -110,6 +110,7 @@ static int __init alsa_seq_init(void) | |||
110 | if ((err = snd_seq_system_client_init()) < 0) | 110 | if ((err = snd_seq_system_client_init()) < 0) |
111 | goto error; | 111 | goto error; |
112 | 112 | ||
113 | snd_seq_autoload_init(); | ||
113 | error: | 114 | error: |
114 | snd_seq_autoload_unlock(); | 115 | snd_seq_autoload_unlock(); |
115 | return err; | 116 | return err; |
@@ -131,6 +132,8 @@ static void __exit alsa_seq_exit(void) | |||
131 | 132 | ||
132 | /* release event memory */ | 133 | /* release event memory */ |
133 | snd_sequencer_memory_done(); | 134 | snd_sequencer_memory_done(); |
135 | |||
136 | snd_seq_autoload_exit(); | ||
134 | } | 137 | } |
135 | 138 | ||
136 | module_init(alsa_seq_init) | 139 | module_init(alsa_seq_init) |
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 775ea9390110..a8e2c6016800 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c | |||
@@ -56,6 +56,7 @@ MODULE_LICENSE("GPL"); | |||
56 | #define DRIVER_LOADED (1<<0) | 56 | #define DRIVER_LOADED (1<<0) |
57 | #define DRIVER_REQUESTED (1<<1) | 57 | #define DRIVER_REQUESTED (1<<1) |
58 | #define DRIVER_LOCKED (1<<2) | 58 | #define DRIVER_LOCKED (1<<2) |
59 | #define DRIVER_REQUESTING (1<<3) | ||
59 | 60 | ||
60 | struct ops_list { | 61 | struct ops_list { |
61 | char id[ID_LEN]; /* driver id */ | 62 | char id[ID_LEN]; /* driver id */ |
@@ -127,7 +128,7 @@ static void snd_seq_device_info(struct snd_info_entry *entry, | |||
127 | 128 | ||
128 | #ifdef CONFIG_MODULES | 129 | #ifdef CONFIG_MODULES |
129 | /* avoid auto-loading during module_init() */ | 130 | /* avoid auto-loading during module_init() */ |
130 | static atomic_t snd_seq_in_init = ATOMIC_INIT(0); | 131 | static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */ |
131 | void snd_seq_autoload_lock(void) | 132 | void snd_seq_autoload_lock(void) |
132 | { | 133 | { |
133 | atomic_inc(&snd_seq_in_init); | 134 | atomic_inc(&snd_seq_in_init); |
@@ -137,32 +138,72 @@ void snd_seq_autoload_unlock(void) | |||
137 | { | 138 | { |
138 | atomic_dec(&snd_seq_in_init); | 139 | atomic_dec(&snd_seq_in_init); |
139 | } | 140 | } |
140 | #endif | ||
141 | 141 | ||
142 | void snd_seq_device_load_drivers(void) | 142 | static void autoload_drivers(void) |
143 | { | 143 | { |
144 | #ifdef CONFIG_MODULES | 144 | /* avoid reentrance */ |
145 | struct ops_list *ops; | 145 | if (atomic_inc_return(&snd_seq_in_init) == 1) { |
146 | struct ops_list *ops; | ||
147 | |||
148 | mutex_lock(&ops_mutex); | ||
149 | list_for_each_entry(ops, &opslist, list) { | ||
150 | if ((ops->driver & DRIVER_REQUESTING) && | ||
151 | !(ops->driver & DRIVER_REQUESTED)) { | ||
152 | ops->used++; | ||
153 | mutex_unlock(&ops_mutex); | ||
154 | ops->driver |= DRIVER_REQUESTED; | ||
155 | request_module("snd-%s", ops->id); | ||
156 | mutex_lock(&ops_mutex); | ||
157 | ops->used--; | ||
158 | } | ||
159 | } | ||
160 | mutex_unlock(&ops_mutex); | ||
161 | } | ||
162 | atomic_dec(&snd_seq_in_init); | ||
163 | } | ||
146 | 164 | ||
147 | /* Calling request_module during module_init() | 165 | static void call_autoload(struct work_struct *work) |
148 | * may cause blocking. | 166 | { |
149 | */ | 167 | autoload_drivers(); |
150 | if (atomic_read(&snd_seq_in_init)) | 168 | } |
151 | return; | ||
152 | 169 | ||
153 | mutex_lock(&ops_mutex); | 170 | static DECLARE_WORK(autoload_work, call_autoload); |
154 | list_for_each_entry(ops, &opslist, list) { | 171 | |
155 | if (! (ops->driver & DRIVER_LOADED) && | 172 | static void try_autoload(struct ops_list *ops) |
156 | ! (ops->driver & DRIVER_REQUESTED)) { | 173 | { |
157 | ops->used++; | 174 | if (!ops->driver) { |
158 | mutex_unlock(&ops_mutex); | 175 | ops->driver |= DRIVER_REQUESTING; |
159 | ops->driver |= DRIVER_REQUESTED; | 176 | schedule_work(&autoload_work); |
160 | request_module("snd-%s", ops->id); | ||
161 | mutex_lock(&ops_mutex); | ||
162 | ops->used--; | ||
163 | } | ||
164 | } | 177 | } |
178 | } | ||
179 | |||
180 | static void queue_autoload_drivers(void) | ||
181 | { | ||
182 | struct ops_list *ops; | ||
183 | |||
184 | mutex_lock(&ops_mutex); | ||
185 | list_for_each_entry(ops, &opslist, list) | ||
186 | try_autoload(ops); | ||
165 | mutex_unlock(&ops_mutex); | 187 | mutex_unlock(&ops_mutex); |
188 | } | ||
189 | |||
190 | void snd_seq_autoload_init(void) | ||
191 | { | ||
192 | atomic_dec(&snd_seq_in_init); | ||
193 | #ifdef CONFIG_SND_SEQUENCER_MODULE | ||
194 | /* initial autoload only when snd-seq is a module */ | ||
195 | queue_autoload_drivers(); | ||
196 | #endif | ||
197 | } | ||
198 | #else | ||
199 | #define try_autoload(ops) /* NOP */ | ||
200 | #endif | ||
201 | |||
202 | void snd_seq_device_load_drivers(void) | ||
203 | { | ||
204 | #ifdef CONFIG_MODULES | ||
205 | queue_autoload_drivers(); | ||
206 | flush_work(&autoload_work); | ||
166 | #endif | 207 | #endif |
167 | } | 208 | } |
168 | 209 | ||
@@ -214,13 +255,14 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, | |||
214 | ops->num_devices++; | 255 | ops->num_devices++; |
215 | mutex_unlock(&ops->reg_mutex); | 256 | mutex_unlock(&ops->reg_mutex); |
216 | 257 | ||
217 | unlock_driver(ops); | ||
218 | |||
219 | if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { | 258 | if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { |
220 | snd_seq_device_free(dev); | 259 | snd_seq_device_free(dev); |
221 | return err; | 260 | return err; |
222 | } | 261 | } |
223 | 262 | ||
263 | try_autoload(ops); | ||
264 | unlock_driver(ops); | ||
265 | |||
224 | if (result) | 266 | if (result) |
225 | *result = dev; | 267 | *result = dev; |
226 | 268 | ||
@@ -554,6 +596,9 @@ static int __init alsa_seq_device_init(void) | |||
554 | 596 | ||
555 | static void __exit alsa_seq_device_exit(void) | 597 | static void __exit alsa_seq_device_exit(void) |
556 | { | 598 | { |
599 | #ifdef CONFIG_MODULES | ||
600 | cancel_work_sync(&autoload_work); | ||
601 | #endif | ||
557 | remove_drivers(); | 602 | remove_drivers(); |
558 | #ifdef CONFIG_PROC_FS | 603 | #ifdef CONFIG_PROC_FS |
559 | snd_info_free_entry(info_entry); | 604 | snd_info_free_entry(info_entry); |
@@ -570,6 +615,7 @@ EXPORT_SYMBOL(snd_seq_device_new); | |||
570 | EXPORT_SYMBOL(snd_seq_device_register_driver); | 615 | EXPORT_SYMBOL(snd_seq_device_register_driver); |
571 | EXPORT_SYMBOL(snd_seq_device_unregister_driver); | 616 | EXPORT_SYMBOL(snd_seq_device_unregister_driver); |
572 | #ifdef CONFIG_MODULES | 617 | #ifdef CONFIG_MODULES |
618 | EXPORT_SYMBOL(snd_seq_autoload_init); | ||
573 | EXPORT_SYMBOL(snd_seq_autoload_lock); | 619 | EXPORT_SYMBOL(snd_seq_autoload_lock); |
574 | EXPORT_SYMBOL(snd_seq_autoload_unlock); | 620 | EXPORT_SYMBOL(snd_seq_autoload_unlock); |
575 | #endif | 621 | #endif |