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:
Takashi Sakamoto 2016-03-31 08:47:07 +09:00 committed by Takashi Iwai
parent 7d3c1d5901
commit 6c29230e2a
2 changed files with 103 additions and 55 deletions

View File

@ -118,15 +118,8 @@ static int name_card(struct snd_oxfw *oxfw)
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,36 +257,66 @@ 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) {
/*
* 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);
goto error;
}
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);
if (oxfw->registered) {
mutex_lock(&oxfw->mutex);
snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
@ -295,14 +327,27 @@ static void oxfw_bus_reset(struct fw_unit *unit)
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);
/*
* 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 = {

View File

@ -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];