diff options
author | Takashi Sakamoto <o-takashi@sakamocchi.jp> | 2016-03-30 19:47:05 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2016-03-31 09:36:18 -0400 |
commit | 04a2c73c97ebb224dfb411ab35bb18d7b8245e39 (patch) | |
tree | d671f334a88b4f9d2eceb1ba3af7cb4370a1cbae /sound/firewire | |
parent | 923f92ebb43e7a09915a5708d4805c1e099db46c (diff) |
ALSA: bebob: delayed registration of sound card
Some bebob based units tends to fail asynchronous communication when
IEEE 1394 bus is under bus-reset state. When registering sound card
instance at unit probe callback, userspace applications can be involved
to the state.
This commit postpones the registration till the bus is calm.
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/firewire')
-rw-r--r-- | sound/firewire/bebob/bebob.c | 215 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob.h | 5 |
2 files changed, 131 insertions, 89 deletions
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 932901de255f..f7e2cbd2a313 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c | |||
@@ -126,6 +126,17 @@ end: | |||
126 | return err; | 126 | return err; |
127 | } | 127 | } |
128 | 128 | ||
129 | static void bebob_free(struct snd_bebob *bebob) | ||
130 | { | ||
131 | snd_bebob_stream_destroy_duplex(bebob); | ||
132 | fw_unit_put(bebob->unit); | ||
133 | |||
134 | kfree(bebob->maudio_special_quirk); | ||
135 | |||
136 | mutex_destroy(&bebob->mutex); | ||
137 | kfree(bebob); | ||
138 | } | ||
139 | |||
129 | /* | 140 | /* |
130 | * This module releases the FireWire unit data after all ALSA character devices | 141 | * This module releases the FireWire unit data after all ALSA character devices |
131 | * are released by applications. This is for releasing stream data or finishing | 142 | * are released by applications. This is for releasing stream data or finishing |
@@ -137,18 +148,11 @@ bebob_card_free(struct snd_card *card) | |||
137 | { | 148 | { |
138 | struct snd_bebob *bebob = card->private_data; | 149 | struct snd_bebob *bebob = card->private_data; |
139 | 150 | ||
140 | snd_bebob_stream_destroy_duplex(bebob); | 151 | mutex_lock(&devices_mutex); |
141 | fw_unit_put(bebob->unit); | 152 | clear_bit(bebob->card_index, devices_used); |
142 | 153 | mutex_unlock(&devices_mutex); | |
143 | kfree(bebob->maudio_special_quirk); | ||
144 | |||
145 | if (bebob->card_index >= 0) { | ||
146 | mutex_lock(&devices_mutex); | ||
147 | clear_bit(bebob->card_index, devices_used); | ||
148 | mutex_unlock(&devices_mutex); | ||
149 | } | ||
150 | 154 | ||
151 | mutex_destroy(&bebob->mutex); | 155 | bebob_free(card->private_data); |
152 | } | 156 | } |
153 | 157 | ||
154 | static const struct snd_bebob_spec * | 158 | static const struct snd_bebob_spec * |
@@ -176,16 +180,17 @@ check_audiophile_booted(struct fw_unit *unit) | |||
176 | return strncmp(name, "FW Audiophile Bootloader", 15) != 0; | 180 | return strncmp(name, "FW Audiophile Bootloader", 15) != 0; |
177 | } | 181 | } |
178 | 182 | ||
179 | static int | 183 | static void |
180 | bebob_probe(struct fw_unit *unit, | 184 | do_registration(struct work_struct *work) |
181 | const struct ieee1394_device_id *entry) | ||
182 | { | 185 | { |
183 | struct snd_card *card; | 186 | struct snd_bebob *bebob = |
184 | struct snd_bebob *bebob; | 187 | container_of(work, struct snd_bebob, dwork.work); |
185 | const struct snd_bebob_spec *spec; | ||
186 | unsigned int card_index; | 188 | unsigned int card_index; |
187 | int err; | 189 | int err; |
188 | 190 | ||
191 | if (bebob->registered) | ||
192 | return; | ||
193 | |||
189 | mutex_lock(&devices_mutex); | 194 | mutex_lock(&devices_mutex); |
190 | 195 | ||
191 | for (card_index = 0; card_index < SNDRV_CARDS; card_index++) { | 196 | for (card_index = 0; card_index < SNDRV_CARDS; card_index++) { |
@@ -193,64 +198,39 @@ bebob_probe(struct fw_unit *unit, | |||
193 | break; | 198 | break; |
194 | } | 199 | } |
195 | if (card_index >= SNDRV_CARDS) { | 200 | if (card_index >= SNDRV_CARDS) { |
196 | err = -ENOENT; | 201 | mutex_unlock(&devices_mutex); |
197 | goto end; | 202 | return; |
198 | } | 203 | } |
199 | 204 | ||
200 | if ((entry->vendor_id == VEN_FOCUSRITE) && | 205 | err = snd_card_new(&bebob->unit->device, index[card_index], |
201 | (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)) | 206 | id[card_index], THIS_MODULE, 0, &bebob->card); |
202 | spec = get_saffire_spec(unit); | 207 | if (err < 0) { |
203 | else if ((entry->vendor_id == VEN_MAUDIO1) && | 208 | mutex_unlock(&devices_mutex); |
204 | (entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) && | 209 | return; |
205 | !check_audiophile_booted(unit)) | ||
206 | spec = NULL; | ||
207 | else | ||
208 | spec = (const struct snd_bebob_spec *)entry->driver_data; | ||
209 | |||
210 | if (spec == NULL) { | ||
211 | if ((entry->vendor_id == VEN_MAUDIO1) || | ||
212 | (entry->vendor_id == VEN_MAUDIO2)) | ||
213 | err = snd_bebob_maudio_load_firmware(unit); | ||
214 | else | ||
215 | err = -ENOSYS; | ||
216 | goto end; | ||
217 | } | 210 | } |
218 | 211 | ||
219 | err = snd_card_new(&unit->device, index[card_index], id[card_index], | ||
220 | THIS_MODULE, sizeof(struct snd_bebob), &card); | ||
221 | if (err < 0) | ||
222 | goto end; | ||
223 | bebob = card->private_data; | ||
224 | bebob->card_index = card_index; | ||
225 | set_bit(card_index, devices_used); | ||
226 | card->private_free = bebob_card_free; | ||
227 | |||
228 | bebob->card = card; | ||
229 | bebob->unit = fw_unit_get(unit); | ||
230 | bebob->spec = spec; | ||
231 | mutex_init(&bebob->mutex); | ||
232 | spin_lock_init(&bebob->lock); | ||
233 | init_waitqueue_head(&bebob->hwdep_wait); | ||
234 | |||
235 | err = name_device(bebob); | 212 | err = name_device(bebob); |
236 | if (err < 0) | 213 | if (err < 0) |
237 | goto error; | 214 | goto error; |
238 | 215 | ||
239 | if ((entry->vendor_id == VEN_MAUDIO1) && | 216 | if (bebob->spec == &maudio_special_spec) { |
240 | (entry->model_id == MODEL_MAUDIO_FW1814)) | 217 | if (bebob->entry->model_id == MODEL_MAUDIO_FW1814) |
241 | err = snd_bebob_maudio_special_discover(bebob, true); | 218 | err = snd_bebob_maudio_special_discover(bebob, true); |
242 | else if ((entry->vendor_id == VEN_MAUDIO1) && | 219 | else |
243 | (entry->model_id == MODEL_MAUDIO_PROJECTMIX)) | 220 | err = snd_bebob_maudio_special_discover(bebob, false); |
244 | err = snd_bebob_maudio_special_discover(bebob, false); | 221 | } else { |
245 | else | ||
246 | err = snd_bebob_stream_discover(bebob); | 222 | err = snd_bebob_stream_discover(bebob); |
223 | } | ||
224 | if (err < 0) | ||
225 | goto error; | ||
226 | |||
227 | err = snd_bebob_stream_init_duplex(bebob); | ||
247 | if (err < 0) | 228 | if (err < 0) |
248 | goto error; | 229 | goto error; |
249 | 230 | ||
250 | snd_bebob_proc_init(bebob); | 231 | snd_bebob_proc_init(bebob); |
251 | 232 | ||
252 | if ((bebob->midi_input_ports > 0) || | 233 | if (bebob->midi_input_ports > 0 || bebob->midi_output_ports > 0) { |
253 | (bebob->midi_output_ports > 0)) { | ||
254 | err = snd_bebob_create_midi_devices(bebob); | 234 | err = snd_bebob_create_midi_devices(bebob); |
255 | if (err < 0) | 235 | if (err < 0) |
256 | goto error; | 236 | goto error; |
@@ -264,16 +244,75 @@ bebob_probe(struct fw_unit *unit, | |||
264 | if (err < 0) | 244 | if (err < 0) |
265 | goto error; | 245 | goto error; |
266 | 246 | ||
267 | err = snd_bebob_stream_init_duplex(bebob); | 247 | err = snd_card_register(bebob->card); |
268 | if (err < 0) | 248 | if (err < 0) |
269 | goto error; | 249 | goto error; |
270 | 250 | ||
271 | if (!bebob->maudio_special_quirk) { | 251 | set_bit(card_index, devices_used); |
272 | err = snd_card_register(card); | 252 | mutex_unlock(&devices_mutex); |
273 | if (err < 0) { | 253 | |
274 | snd_bebob_stream_destroy_duplex(bebob); | 254 | /* |
275 | goto error; | 255 | * After registered, bebob instance can be released corresponding to |
276 | } | 256 | * releasing the sound card instance. |
257 | */ | ||
258 | bebob->card->private_free = bebob_card_free; | ||
259 | bebob->card->private_data = bebob; | ||
260 | bebob->registered = true; | ||
261 | |||
262 | return; | ||
263 | error: | ||
264 | mutex_unlock(&devices_mutex); | ||
265 | snd_bebob_stream_destroy_duplex(bebob); | ||
266 | snd_card_free(bebob->card); | ||
267 | dev_info(&bebob->unit->device, | ||
268 | "Sound card registration failed: %d\n", err); | ||
269 | } | ||
270 | |||
271 | static int | ||
272 | bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry) | ||
273 | { | ||
274 | struct snd_bebob *bebob; | ||
275 | const struct snd_bebob_spec *spec; | ||
276 | |||
277 | if (entry->vendor_id == VEN_FOCUSRITE && | ||
278 | entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH) | ||
279 | spec = get_saffire_spec(unit); | ||
280 | else if (entry->vendor_id == VEN_MAUDIO1 && | ||
281 | entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH && | ||
282 | !check_audiophile_booted(unit)) | ||
283 | spec = NULL; | ||
284 | else | ||
285 | spec = (const struct snd_bebob_spec *)entry->driver_data; | ||
286 | |||
287 | if (spec == NULL) { | ||
288 | if (entry->vendor_id == VEN_MAUDIO1 || | ||
289 | entry->vendor_id == VEN_MAUDIO2) | ||
290 | return snd_bebob_maudio_load_firmware(unit); | ||
291 | else | ||
292 | return -ENODEV; | ||
293 | } | ||
294 | |||
295 | /* Allocate this independent of sound card instance. */ | ||
296 | bebob = kzalloc(sizeof(struct snd_bebob), GFP_KERNEL); | ||
297 | if (bebob == NULL) | ||
298 | return -ENOMEM; | ||
299 | |||
300 | bebob->unit = fw_unit_get(unit); | ||
301 | bebob->entry = entry; | ||
302 | bebob->spec = spec; | ||
303 | dev_set_drvdata(&unit->device, bebob); | ||
304 | |||
305 | mutex_init(&bebob->mutex); | ||
306 | spin_lock_init(&bebob->lock); | ||
307 | init_waitqueue_head(&bebob->hwdep_wait); | ||
308 | |||
309 | /* Allocate and register this sound card later. */ | ||
310 | INIT_DEFERRABLE_WORK(&bebob->dwork, do_registration); | ||
311 | |||
312 | if (entry->vendor_id != VEN_MAUDIO1 || | ||
313 | (entry->model_id != MODEL_MAUDIO_FW1814 && | ||
314 | entry->model_id != MODEL_MAUDIO_PROJECTMIX)) { | ||
315 | snd_fw_schedule_registration(unit, &bebob->dwork); | ||
277 | } else { | 316 | } else { |
278 | /* | 317 | /* |
279 | * This is a workaround. This bus reset seems to have an effect | 318 | * This is a workaround. This bus reset seems to have an effect |
@@ -285,19 +324,11 @@ bebob_probe(struct fw_unit *unit, | |||
285 | * signals from dbus and starts I/Os. To avoid I/Os till the | 324 | * signals from dbus and starts I/Os. To avoid I/Os till the |
286 | * future bus reset, registration is done in next update(). | 325 | * future bus reset, registration is done in next update(). |
287 | */ | 326 | */ |
288 | bebob->deferred_registration = true; | ||
289 | fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card, | 327 | fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card, |
290 | false, true); | 328 | false, true); |
291 | } | 329 | } |
292 | 330 | ||
293 | dev_set_drvdata(&unit->device, bebob); | 331 | return 0; |
294 | end: | ||
295 | mutex_unlock(&devices_mutex); | ||
296 | return err; | ||
297 | error: | ||
298 | mutex_unlock(&devices_mutex); | ||
299 | snd_card_free(card); | ||
300 | return err; | ||
301 | } | 332 | } |
302 | 333 | ||
303 | /* | 334 | /* |
@@ -324,15 +355,11 @@ bebob_update(struct fw_unit *unit) | |||
324 | if (bebob == NULL) | 355 | if (bebob == NULL) |
325 | return; | 356 | return; |
326 | 357 | ||
327 | fcp_bus_reset(bebob->unit); | 358 | /* Postpone a workqueue for deferred registration. */ |
328 | 359 | if (!bebob->registered) | |
329 | if (bebob->deferred_registration) { | 360 | snd_fw_schedule_registration(unit, &bebob->dwork); |
330 | if (snd_card_register(bebob->card) < 0) { | 361 | else |
331 | snd_bebob_stream_destroy_duplex(bebob); | 362 | fcp_bus_reset(bebob->unit); |
332 | snd_card_free(bebob->card); | ||
333 | } | ||
334 | bebob->deferred_registration = false; | ||
335 | } | ||
336 | } | 363 | } |
337 | 364 | ||
338 | static void bebob_remove(struct fw_unit *unit) | 365 | static void bebob_remove(struct fw_unit *unit) |
@@ -342,8 +369,20 @@ static void bebob_remove(struct fw_unit *unit) | |||
342 | if (bebob == NULL) | 369 | if (bebob == NULL) |
343 | return; | 370 | return; |
344 | 371 | ||
345 | /* No need to wait for releasing card object in this context. */ | 372 | /* |
346 | snd_card_free_when_closed(bebob->card); | 373 | * Confirm to stop the work for registration before the sound card is |
374 | * going to be released. The work is not scheduled again because bus | ||
375 | * reset handler is not called anymore. | ||
376 | */ | ||
377 | cancel_delayed_work_sync(&bebob->dwork); | ||
378 | |||
379 | if (bebob->registered) { | ||
380 | /* No need to wait for releasing card object in this context. */ | ||
381 | snd_card_free_when_closed(bebob->card); | ||
382 | } else { | ||
383 | /* Don't forget this case. */ | ||
384 | bebob_free(bebob); | ||
385 | } | ||
347 | } | 386 | } |
348 | 387 | ||
349 | static const struct snd_bebob_rate_spec normal_rate_spec = { | 388 | static const struct snd_bebob_rate_spec normal_rate_spec = { |
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index b50bb33d9d46..2a442a7a2119 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h | |||
@@ -83,6 +83,10 @@ struct snd_bebob { | |||
83 | struct mutex mutex; | 83 | struct mutex mutex; |
84 | spinlock_t lock; | 84 | spinlock_t lock; |
85 | 85 | ||
86 | bool registered; | ||
87 | struct delayed_work dwork; | ||
88 | |||
89 | const struct ieee1394_device_id *entry; | ||
86 | const struct snd_bebob_spec *spec; | 90 | const struct snd_bebob_spec *spec; |
87 | 91 | ||
88 | unsigned int midi_input_ports; | 92 | unsigned int midi_input_ports; |
@@ -111,7 +115,6 @@ struct snd_bebob { | |||
111 | 115 | ||
112 | /* for M-Audio special devices */ | 116 | /* for M-Audio special devices */ |
113 | void *maudio_special_quirk; | 117 | void *maudio_special_quirk; |
114 | bool deferred_registration; | ||
115 | 118 | ||
116 | /* For BeBoB version quirk. */ | 119 | /* For BeBoB version quirk. */ |
117 | unsigned int version; | 120 | unsigned int version; |