Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+98 −53
Original line number Original line Diff line number Diff line
@@ -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,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,36 +257,66 @@ 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)
		goto error;
	if (oxfw->has_output) {
		err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
	if (err < 0)
	if (err < 0)
		goto error;
		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);
	snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
	if (oxfw->has_output)
	if (oxfw->has_output)
		snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
		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);
	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);


	if (oxfw->registered) {
		mutex_lock(&oxfw->mutex);
		mutex_lock(&oxfw->mutex);


		snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
		snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
@@ -296,13 +328,26 @@ static void oxfw_bus_reset(struct fw_unit *unit)
		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);


	/*
	 * 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. */
		/* No need to wait for releasing card object in this context. */
		snd_card_free_when_closed(oxfw->card);
		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 = {
+3 −0
Original line number Original line Diff line number Diff line
@@ -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];