Commit 6c29230e authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

ALSA: oxfw: delayed registration of sound card

Some oxfw 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: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 7d3c1d59
...@@ -118,15 +118,8 @@ static int name_card(struct snd_oxfw *oxfw) ...@@ -118,15 +118,8 @@ static int name_card(struct snd_oxfw *oxfw)
return err; return err;
} }
/* static void oxfw_free(struct snd_oxfw *oxfw)
* This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing
* transactions safely. Thus at returning from .remove(), this module still keep
* references for the unit.
*/
static void oxfw_card_free(struct snd_card *card)
{ {
struct snd_oxfw *oxfw = card->private_data;
unsigned int i; unsigned int i;
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
...@@ -144,6 +137,17 @@ static void oxfw_card_free(struct snd_card *card) ...@@ -144,6 +137,17 @@ static void oxfw_card_free(struct snd_card *card)
mutex_destroy(&oxfw->mutex); mutex_destroy(&oxfw->mutex);
} }
/*
* This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing
* transactions safely. Thus at returning from .remove(), this module still keep
* references for the unit.
*/
static void oxfw_card_free(struct snd_card *card)
{
oxfw_free(card->private_data);
}
static int detect_quirks(struct snd_oxfw *oxfw) static int detect_quirks(struct snd_oxfw *oxfw)
{ {
struct fw_device *fw_dev = fw_parent_device(oxfw->unit); struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
...@@ -205,41 +209,39 @@ static int detect_quirks(struct snd_oxfw *oxfw) ...@@ -205,41 +209,39 @@ static int detect_quirks(struct snd_oxfw *oxfw)
return 0; return 0;
} }
static int oxfw_probe(struct fw_unit *unit, static void do_registration(struct work_struct *work)
const struct ieee1394_device_id *entry)
{ {
struct snd_card *card; struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
struct snd_oxfw *oxfw;
int err; int err;
if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit)) if (oxfw->registered)
return -ENODEV; return;
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
sizeof(*oxfw), &card); &oxfw->card);
if (err < 0) if (err < 0)
return err; return;
card->private_free = oxfw_card_free; err = name_card(oxfw);
oxfw = card->private_data; if (err < 0)
oxfw->card = card; goto error;
mutex_init(&oxfw->mutex);
oxfw->unit = fw_unit_get(unit);
oxfw->entry = entry;
spin_lock_init(&oxfw->lock);
init_waitqueue_head(&oxfw->hwdep_wait);
err = snd_oxfw_stream_discover(oxfw); err = detect_quirks(oxfw);
if (err < 0) if (err < 0)
goto error; goto error;
err = name_card(oxfw); err = snd_oxfw_stream_discover(oxfw);
if (err < 0) if (err < 0)
goto error; goto error;
err = detect_quirks(oxfw); err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
if (err < 0) if (err < 0)
goto error; goto error;
if (oxfw->has_output) {
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
if (err < 0)
goto error;
}
err = snd_oxfw_create_pcm(oxfw); err = snd_oxfw_create_pcm(oxfw);
if (err < 0) if (err < 0)
...@@ -255,54 +257,97 @@ static int oxfw_probe(struct fw_unit *unit, ...@@ -255,54 +257,97 @@ static int oxfw_probe(struct fw_unit *unit,
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream); err = snd_card_register(oxfw->card);
if (err < 0) if (err < 0)
goto error; goto error;
if (oxfw->has_output) {
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
if (err < 0)
goto error;
}
err = snd_card_register(card); /*
if (err < 0) { * After registered, oxfw instance can be released corresponding to
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); * releasing the sound card instance.
if (oxfw->has_output) */
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); oxfw->card->private_free = oxfw_card_free;
goto error; oxfw->card->private_data = oxfw;
} oxfw->registered = true;
return;
error:
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
if (oxfw->has_output)
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
snd_card_free(oxfw->card);
dev_info(&oxfw->unit->device,
"Sound card registration failed: %d\n", err);
}
static int oxfw_probe(struct fw_unit *unit,
const struct ieee1394_device_id *entry)
{
struct snd_oxfw *oxfw;
if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
return -ENODEV;
/* Allocate this independent of sound card instance. */
oxfw = kzalloc(sizeof(struct snd_oxfw), GFP_KERNEL);
if (oxfw == NULL)
return -ENOMEM;
oxfw->entry = entry;
oxfw->unit = fw_unit_get(unit);
dev_set_drvdata(&unit->device, oxfw); dev_set_drvdata(&unit->device, oxfw);
mutex_init(&oxfw->mutex);
spin_lock_init(&oxfw->lock);
init_waitqueue_head(&oxfw->hwdep_wait);
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration);
snd_fw_schedule_registration(unit, &oxfw->dwork);
return 0; return 0;
error:
snd_card_free(card);
return err;
} }
static void oxfw_bus_reset(struct fw_unit *unit) static void oxfw_bus_reset(struct fw_unit *unit)
{ {
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
if (!oxfw->registered)
snd_fw_schedule_registration(unit, &oxfw->dwork);
fcp_bus_reset(oxfw->unit); fcp_bus_reset(oxfw->unit);
mutex_lock(&oxfw->mutex); if (oxfw->registered) {
mutex_lock(&oxfw->mutex);
snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream); snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
if (oxfw->has_output) if (oxfw->has_output)
snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream); snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
mutex_unlock(&oxfw->mutex); mutex_unlock(&oxfw->mutex);
if (oxfw->entry->vendor_id == OUI_STANTON) if (oxfw->entry->vendor_id == OUI_STANTON)
snd_oxfw_scs1x_update(oxfw); snd_oxfw_scs1x_update(oxfw);
}
} }
static void oxfw_remove(struct fw_unit *unit) static void oxfw_remove(struct fw_unit *unit)
{ {
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
/* No need to wait for releasing card object in this context. */ /*
snd_card_free_when_closed(oxfw->card); * Confirm to stop the work for registration before the sound card is
* going to be released. The work is not scheduled again because bus
* reset handler is not called anymore.
*/
cancel_delayed_work_sync(&oxfw->dwork);
if (oxfw->registered) {
/* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(oxfw->card);
} else {
/* Don't forget this case. */
oxfw_free(oxfw);
}
} }
static const struct compat_info griffin_firewave = { static const struct compat_info griffin_firewave = {
......
...@@ -39,6 +39,9 @@ struct snd_oxfw { ...@@ -39,6 +39,9 @@ struct snd_oxfw {
struct mutex mutex; struct mutex mutex;
spinlock_t lock; spinlock_t lock;
bool registered;
struct delayed_work dwork;
bool wrong_dbs; bool wrong_dbs;
bool has_output; bool has_output;
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES]; u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment