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: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
7d3c1d5901
commit
6c29230e2a
2 changed files with 103 additions and 55 deletions
|
@ -118,15 +118,8 @@ end:
|
|||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
static void oxfw_free(struct snd_oxfw *oxfw)
|
||||
{
|
||||
struct snd_oxfw *oxfw = card->private_data;
|
||||
unsigned int i;
|
||||
|
||||
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
|
||||
|
@ -144,6 +137,17 @@ static void oxfw_card_free(struct snd_card *card)
|
|||
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)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
|
||||
|
@ -205,33 +209,18 @@ static int detect_quirks(struct snd_oxfw *oxfw)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int oxfw_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
static void do_registration(struct work_struct *work)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_oxfw *oxfw;
|
||||
struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
|
||||
int err;
|
||||
|
||||
if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
|
||||
return -ENODEV;
|
||||
if (oxfw->registered)
|
||||
return;
|
||||
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
|
||||
sizeof(*oxfw), &card);
|
||||
err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
|
||||
&oxfw->card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
card->private_free = oxfw_card_free;
|
||||
oxfw = card->private_data;
|
||||
oxfw->card = card;
|
||||
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);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
return;
|
||||
|
||||
err = name_card(oxfw);
|
||||
if (err < 0)
|
||||
|
@ -241,6 +230,19 @@ static int oxfw_probe(struct fw_unit *unit,
|
|||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_oxfw_stream_discover(oxfw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
|
||||
if (err < 0)
|
||||
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);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
@ -255,54 +257,97 @@ static int oxfw_probe(struct fw_unit *unit,
|
|||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
|
||||
err = snd_card_register(oxfw->card);
|
||||
if (err < 0)
|
||||
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) {
|
||||
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
|
||||
if (oxfw->has_output)
|
||||
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
|
||||
goto error;
|
||||
}
|
||||
/*
|
||||
* After registered, oxfw instance can be released corresponding to
|
||||
* releasing the sound card instance.
|
||||
*/
|
||||
oxfw->card->private_free = oxfw_card_free;
|
||||
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);
|
||||
|
||||
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;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void oxfw_bus_reset(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
|
||||
|
||||
if (!oxfw->registered)
|
||||
snd_fw_schedule_registration(unit, &oxfw->dwork);
|
||||
|
||||
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);
|
||||
if (oxfw->has_output)
|
||||
snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
|
||||
snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
|
||||
if (oxfw->has_output)
|
||||
snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
|
||||
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
|
||||
if (oxfw->entry->vendor_id == OUI_STANTON)
|
||||
snd_oxfw_scs1x_update(oxfw);
|
||||
if (oxfw->entry->vendor_id == OUI_STANTON)
|
||||
snd_oxfw_scs1x_update(oxfw);
|
||||
}
|
||||
}
|
||||
|
||||
static void oxfw_remove(struct fw_unit *unit)
|
||||
{
|
||||
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 = {
|
||||
|
|
|
@ -39,6 +39,9 @@ struct snd_oxfw {
|
|||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
bool wrong_dbs;
|
||||
bool has_output;
|
||||
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
|
||||
|
|
Loading…
Reference in a new issue