mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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 @@ 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,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)
 | 
						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
 | 
				
			||||||
 | 
						 * 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);
 | 
				
			||||||
| 
						 | 
					@ -295,14 +327,27 @@ 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 = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 a new issue