mirror of https://gitee.com/openkylin/linux.git
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
|
@ -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)
|
||||||
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,33 +209,18 @@ 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;
|
|
||||||
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;
|
|
||||||
|
|
||||||
err = name_card(oxfw);
|
err = name_card(oxfw);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
@ -241,6 +230,19 @@ static int oxfw_probe(struct fw_unit *unit,
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto error;
|
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);
|
err = snd_oxfw_create_pcm(oxfw);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -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 {
|
||||||
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];
|
||||||
|
|
Loading…
Reference in New Issue