mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	ALSA: core: Add managed card creation
As a second step for preliminary to widen the devres usages among sound drivers, this patch adds a new ALSA core API function, snd_devm_card_new(), to create a snd_card object via devres. When a card object is created by this new function, snd_card_free() is called automatically and the card object resource gets released at the device unbinding time. However, the story isn't that simple. A caveat is that we have to call snd_card_free() at the very first of the whole resource release procedure, in order to assure that the all exposed devices on user-space are deleted and sync with processes accessing those devices before releasing resources. For achieving it, snd_card_register() adds a new devres action to trigger snd_card_free() automatically when the given card object is a "managed" one. Since usually snd_card_register() is the last step of the initialization, this should work in most cases. With all these tricks, some drivers can get rid of the whole driver remove callback code. About a bit of implementation details: the patch adds two new flags to snd_card object: managed and releasing. The former indicates that the object was created via snd_devm_card_new(), and the latter is used for avoiding the double-free of snd_card_free() calls. Both flags are fairly internal and likely uninteresting to normal users. Link: https://lore.kernel.org/r/20210715075941.23332-3-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
		
							parent
							
								
									427ae2689d
								
							
						
					
					
						commit
						e8ad415b7a
					
				
					 2 changed files with 96 additions and 8 deletions
				
			
		| 
						 | 
					@ -117,6 +117,8 @@ struct snd_card {
 | 
				
			||||||
	struct device card_dev;		/* cardX object for sysfs */
 | 
						struct device card_dev;		/* cardX object for sysfs */
 | 
				
			||||||
	const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
 | 
						const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
 | 
				
			||||||
	bool registered;		/* card_dev is registered? */
 | 
						bool registered;		/* card_dev is registered? */
 | 
				
			||||||
 | 
						bool managed;			/* managed via devres */
 | 
				
			||||||
 | 
						bool releasing;			/* during card free process */
 | 
				
			||||||
	int sync_irq;			/* assigned irq, used for PCM sync */
 | 
						int sync_irq;			/* assigned irq, used for PCM sync */
 | 
				
			||||||
	wait_queue_head_t remove_sleep;
 | 
						wait_queue_head_t remove_sleep;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -274,6 +276,9 @@ extern int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int cmd);
 | 
				
			||||||
int snd_card_new(struct device *parent, int idx, const char *xid,
 | 
					int snd_card_new(struct device *parent, int idx, const char *xid,
 | 
				
			||||||
		 struct module *module, int extra_size,
 | 
							 struct module *module, int extra_size,
 | 
				
			||||||
		 struct snd_card **card_ret);
 | 
							 struct snd_card **card_ret);
 | 
				
			||||||
 | 
					int snd_devm_card_new(struct device *parent, int idx, const char *xid,
 | 
				
			||||||
 | 
							      struct module *module, size_t extra_size,
 | 
				
			||||||
 | 
							      struct snd_card **card_ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int snd_card_disconnect(struct snd_card *card);
 | 
					int snd_card_disconnect(struct snd_card *card);
 | 
				
			||||||
void snd_card_disconnect_sync(struct snd_card *card);
 | 
					void snd_card_disconnect_sync(struct snd_card *card);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -134,6 +134,9 @@ void snd_device_initialize(struct device *dev, struct snd_card *card)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(snd_device_initialize);
 | 
					EXPORT_SYMBOL_GPL(snd_device_initialize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int snd_card_init(struct snd_card *card, struct device *parent,
 | 
				
			||||||
 | 
								 int idx, const char *xid, struct module *module,
 | 
				
			||||||
 | 
								 size_t extra_size);
 | 
				
			||||||
static int snd_card_do_free(struct snd_card *card);
 | 
					static int snd_card_do_free(struct snd_card *card);
 | 
				
			||||||
static const struct attribute_group card_dev_attr_group;
 | 
					static const struct attribute_group card_dev_attr_group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -163,9 +166,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct snd_card *card;
 | 
						struct snd_card *card;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
#ifdef CONFIG_SND_DEBUG
 | 
					 | 
				
			||||||
	char name[8];
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (snd_BUG_ON(!card_ret))
 | 
						if (snd_BUG_ON(!card_ret))
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
| 
						 | 
					@ -176,6 +176,74 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
 | 
				
			||||||
	card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
 | 
						card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
 | 
				
			||||||
	if (!card)
 | 
						if (!card)
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = snd_card_init(card, parent, idx, xid, module, extra_size);
 | 
				
			||||||
 | 
						if (err < 0) {
 | 
				
			||||||
 | 
							kfree(card);
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*card_ret = card;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(snd_card_new);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void __snd_card_release(struct device *dev, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						snd_card_free(data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * snd_devm_card_new - managed snd_card object creation
 | 
				
			||||||
 | 
					 * @parent: the parent device object
 | 
				
			||||||
 | 
					 * @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
 | 
				
			||||||
 | 
					 * @xid: card identification (ASCII string)
 | 
				
			||||||
 | 
					 * @module: top level module for locking
 | 
				
			||||||
 | 
					 * @extra_size: allocate this extra size after the main soundcard structure
 | 
				
			||||||
 | 
					 * @card_ret: the pointer to store the created card instance
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This function works like snd_card_new() but manages the allocated resource
 | 
				
			||||||
 | 
					 * via devres, i.e. you don't need to free explicitly.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * When a snd_card object is created with this function and registered via
 | 
				
			||||||
 | 
					 * snd_card_register(), the very first devres action to call snd_card_free()
 | 
				
			||||||
 | 
					 * is added automatically.  In that way, the resource disconnection is assured
 | 
				
			||||||
 | 
					 * at first, then released in the expected order.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int snd_devm_card_new(struct device *parent, int idx, const char *xid,
 | 
				
			||||||
 | 
							      struct module *module, size_t extra_size,
 | 
				
			||||||
 | 
							      struct snd_card **card_ret)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct snd_card *card;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*card_ret = NULL;
 | 
				
			||||||
 | 
						card = devres_alloc(__snd_card_release, sizeof(*card) + extra_size,
 | 
				
			||||||
 | 
								    GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!card)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						card->managed = true;
 | 
				
			||||||
 | 
						err = snd_card_init(card, parent, idx, xid, module, extra_size);
 | 
				
			||||||
 | 
						if (err < 0) {
 | 
				
			||||||
 | 
							devres_free(card);
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						devres_add(parent, card);
 | 
				
			||||||
 | 
						*card_ret = card;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(snd_devm_card_new);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int snd_card_init(struct snd_card *card, struct device *parent,
 | 
				
			||||||
 | 
								 int idx, const char *xid, struct module *module,
 | 
				
			||||||
 | 
								 size_t extra_size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					#ifdef CONFIG_SND_DEBUG
 | 
				
			||||||
 | 
						char name[8];
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (extra_size > 0)
 | 
						if (extra_size > 0)
 | 
				
			||||||
		card->private_data = (char *)card + sizeof(struct snd_card);
 | 
							card->private_data = (char *)card + sizeof(struct snd_card);
 | 
				
			||||||
	if (xid)
 | 
						if (xid)
 | 
				
			||||||
| 
						 | 
					@ -197,7 +265,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
 | 
				
			||||||
		mutex_unlock(&snd_card_mutex);
 | 
							mutex_unlock(&snd_card_mutex);
 | 
				
			||||||
		dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
 | 
							dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
 | 
				
			||||||
			 idx, snd_ecards_limit - 1, err);
 | 
								 idx, snd_ecards_limit - 1, err);
 | 
				
			||||||
		kfree(card);
 | 
					 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	set_bit(idx, snd_cards_lock);		/* lock it */
 | 
						set_bit(idx, snd_cards_lock);		/* lock it */
 | 
				
			||||||
| 
						 | 
					@ -256,8 +323,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
 | 
				
			||||||
	sprintf(name, "card%d", idx);
 | 
						sprintf(name, "card%d", idx);
 | 
				
			||||||
	card->debugfs_root = debugfs_create_dir(name, sound_debugfs_root);
 | 
						card->debugfs_root = debugfs_create_dir(name, sound_debugfs_root);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					 | 
				
			||||||
	*card_ret = card;
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      __error_ctl:
 | 
					      __error_ctl:
 | 
				
			||||||
| 
						 | 
					@ -266,7 +331,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
 | 
				
			||||||
	put_device(&card->card_dev);
 | 
						put_device(&card->card_dev);
 | 
				
			||||||
  	return err;
 | 
					  	return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(snd_card_new);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * snd_card_ref - Get the card object from the index
 | 
					 * snd_card_ref - Get the card object from the index
 | 
				
			||||||
| 
						 | 
					@ -481,6 +545,7 @@ EXPORT_SYMBOL_GPL(snd_card_disconnect_sync);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int snd_card_do_free(struct snd_card *card)
 | 
					static int snd_card_do_free(struct snd_card *card)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						card->releasing = true;
 | 
				
			||||||
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
 | 
					#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
 | 
				
			||||||
	if (snd_mixer_oss_notify_callback)
 | 
						if (snd_mixer_oss_notify_callback)
 | 
				
			||||||
		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
 | 
							snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
 | 
				
			||||||
| 
						 | 
					@ -498,7 +563,8 @@ static int snd_card_do_free(struct snd_card *card)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	if (card->release_completion)
 | 
						if (card->release_completion)
 | 
				
			||||||
		complete(card->release_completion);
 | 
							complete(card->release_completion);
 | 
				
			||||||
	kfree(card);
 | 
						if (!card->managed)
 | 
				
			||||||
 | 
							kfree(card);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -745,6 +811,14 @@ int snd_card_add_dev_attr(struct snd_card *card,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(snd_card_add_dev_attr);
 | 
					EXPORT_SYMBOL_GPL(snd_card_add_dev_attr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void trigger_card_free(void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct snd_card *card = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!card->releasing)
 | 
				
			||||||
 | 
							snd_card_free(data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 *  snd_card_register - register the soundcard
 | 
					 *  snd_card_register - register the soundcard
 | 
				
			||||||
 *  @card: soundcard structure
 | 
					 *  @card: soundcard structure
 | 
				
			||||||
| 
						 | 
					@ -768,6 +842,15 @@ int snd_card_register(struct snd_card *card)
 | 
				
			||||||
		if (err < 0)
 | 
							if (err < 0)
 | 
				
			||||||
			return err;
 | 
								return err;
 | 
				
			||||||
		card->registered = true;
 | 
							card->registered = true;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if (card->managed)
 | 
				
			||||||
 | 
								devm_remove_action(card->dev, trigger_card_free, card);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (card->managed) {
 | 
				
			||||||
 | 
							err = devm_add_action(card->dev, trigger_card_free, card);
 | 
				
			||||||
 | 
							if (err < 0)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = snd_device_register_all(card);
 | 
						err = snd_device_register_all(card);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue