diff options
Diffstat (limited to 'sound/core/init.c')
| -rw-r--r-- | sound/core/init.c | 433 | 
1 files changed, 238 insertions, 195 deletions
diff --git a/sound/core/init.c b/sound/core/init.c index 57b792e2439..7bdfd19e24a 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -21,11 +21,14 @@  #include <linux/init.h>  #include <linux/sched.h> +#include <linux/module.h> +#include <linux/device.h>  #include <linux/file.h>  #include <linux/slab.h>  #include <linux/time.h>  #include <linux/ctype.h>  #include <linux/pm.h> +#include <linux/completion.h>  #include <sound/core.h>  #include <sound/control.h> @@ -44,7 +47,8 @@ static LIST_HEAD(shutdown_files);  static const struct file_operations snd_shutdown_f_ops; -static unsigned int snd_cards_lock;	/* locked for registering/using */ +/* locked for registering/using */ +static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS);  struct snd_card *snd_cards[SNDRV_CARDS];  EXPORT_SYMBOL(snd_cards); @@ -63,7 +67,7 @@ static int module_slot_match(struct module *module, int idx)  #ifdef MODULE  	const char *s1, *s2; -	if (!module || !module->name || !slots[idx]) +	if (!module || !*module->name || !slots[idx])  		return 0;  	s1 = module->name; @@ -91,7 +95,7 @@ static int module_slot_match(struct module *module, int idx)  	return match;  } -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS)  int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);  EXPORT_SYMBOL(snd_mixer_oss_notify_callback);  #endif @@ -109,11 +113,11 @@ static inline int init_info_for_card(struct snd_card *card)  	struct snd_info_entry *entry;  	if ((err = snd_info_card_register(card)) < 0) { -		snd_printd("unable to create card info\n"); +		dev_dbg(card->dev, "unable to create card info\n");  		return err;  	}  	if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { -		snd_printd("unable to create card entry\n"); +		dev_dbg(card->dev, "unable to create card entry\n");  		return err;  	}  	entry->c.text.read = snd_card_id_read; @@ -128,8 +132,42 @@ static inline int init_info_for_card(struct snd_card *card)  #define init_info_for_card(card)  #endif +static int check_empty_slot(struct module *module, int slot) +{ +	return !slots[slot] || !*slots[slot]; +} + +/* return an empty slot number (>= 0) found in the given bitmask @mask. + * @mask == -1 == 0xffffffff means: take any free slot up to 32 + * when no slot is available, return the original @mask as is. + */ +static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int), +				 struct module *module) +{ +	int slot; + +	for (slot = 0; slot < SNDRV_CARDS; slot++) { +		if (slot < 32 && !(mask & (1U << slot))) +			continue; +		if (!test_bit(slot, snd_cards_lock)) { +			if (check(module, slot)) +				return slot; /* found */ +		} +	} +	return mask; /* unchanged */ +} + +static int snd_card_do_free(struct snd_card *card); +static const struct attribute_group *card_dev_attr_groups[]; + +static void release_card_device(struct device *dev) +{ +	snd_card_do_free(dev_to_snd_card(dev)); +} +  /** - *  snd_card_create - create and initialize a soundcard structure + *  snd_card_new - create and initialize a soundcard structure + *  @parent: the parent device object   *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]   *  @xid: card identification (ASCII string)   *  @module: top level module for locking @@ -142,14 +180,14 @@ static inline int init_info_for_card(struct snd_card *card)   *  space for the driver to use freely.  The allocated struct is stored   *  in the given card_ret pointer.   * - *  Returns zero if successful or a negative error code. + *  Return: Zero if successful or a negative error code.   */ -int snd_card_create(int idx, const char *xid, +int snd_card_new(struct device *parent, int idx, const char *xid,  		    struct module *module, int extra_size,  		    struct snd_card **card_ret)  {  	struct snd_card *card; -	int err, idx2; +	int err;  	if (snd_BUG_ON(!card_ret))  		return -EINVAL; @@ -160,85 +198,81 @@ int snd_card_create(int idx, const char *xid,  	card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);  	if (!card)  		return -ENOMEM; +	if (extra_size > 0) +		card->private_data = (char *)card + sizeof(struct snd_card);  	if (xid)  		strlcpy(card->id, xid, sizeof(card->id));  	err = 0;  	mutex_lock(&snd_card_mutex); -	if (idx < 0) { -		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) -			/* idx == -1 == 0xffff means: take any free slot */ -			if (~snd_cards_lock & idx & 1<<idx2) { -				if (module_slot_match(module, idx2)) { -					idx = idx2; -					break; -				} -			} -	} -	if (idx < 0) { -		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) -			/* idx == -1 == 0xffff means: take any free slot */ -			if (~snd_cards_lock & idx & 1<<idx2) { -				if (!slots[idx2] || !*slots[idx2]) { -					idx = idx2; -					break; -				} -			} -	} +	if (idx < 0) /* first check the matching module-name slot */ +		idx = get_slot_from_bitmask(idx, module_slot_match, module); +	if (idx < 0) /* if not matched, assign an empty slot */ +		idx = get_slot_from_bitmask(idx, check_empty_slot, module);  	if (idx < 0)  		err = -ENODEV;  	else if (idx < snd_ecards_limit) { -		if (snd_cards_lock & (1 << idx)) +		if (test_bit(idx, snd_cards_lock))  			err = -EBUSY;	/* invalid */  	} else if (idx >= SNDRV_CARDS)  		err = -ENODEV;  	if (err < 0) {  		mutex_unlock(&snd_card_mutex); -		snd_printk(KERN_ERR "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); -		goto __error; +		kfree(card); +		return err;  	} -	snd_cards_lock |= 1 << idx;		/* lock it */ +	set_bit(idx, snd_cards_lock);		/* lock it */  	if (idx >= snd_ecards_limit)  		snd_ecards_limit = idx + 1; /* increase the limit */  	mutex_unlock(&snd_card_mutex); +	card->dev = parent;  	card->number = idx;  	card->module = module;  	INIT_LIST_HEAD(&card->devices);  	init_rwsem(&card->controls_rwsem);  	rwlock_init(&card->ctl_files_rwlock); +	mutex_init(&card->user_ctl_lock);  	INIT_LIST_HEAD(&card->controls);  	INIT_LIST_HEAD(&card->ctl_files);  	spin_lock_init(&card->files_lock);  	INIT_LIST_HEAD(&card->files_list); -	init_waitqueue_head(&card->shutdown_sleep);  #ifdef CONFIG_PM  	mutex_init(&card->power_lock);  	init_waitqueue_head(&card->power_sleep);  #endif + +	device_initialize(&card->card_dev); +	card->card_dev.parent = parent; +	card->card_dev.class = sound_class; +	card->card_dev.release = release_card_device; +	card->card_dev.groups = card_dev_attr_groups; +	err = kobject_set_name(&card->card_dev.kobj, "card%d", idx); +	if (err < 0) +		goto __error; +  	/* the control interface cannot be accessed from the user space until */  	/* snd_cards_bitmask and snd_cards are set with snd_card_register */  	err = snd_ctl_create(card);  	if (err < 0) { -		snd_printk(KERN_ERR "unable to register control minors\n"); +		dev_err(parent, "unable to register control minors\n");  		goto __error;  	}  	err = snd_info_card_create(card);  	if (err < 0) { -		snd_printk(KERN_ERR "unable to create card info\n"); +		dev_err(parent, "unable to create card info\n");  		goto __error_ctl;  	} -	if (extra_size > 0) -		card->private_data = (char *)card + sizeof(struct snd_card);  	*card_ret = card;  	return 0;        __error_ctl: -	snd_device_free_all(card, SNDRV_DEV_CMD_PRE); +	snd_device_free_all(card);        __error: -	kfree(card); +	put_device(&card->card_dev);    	return err;  } -EXPORT_SYMBOL(snd_card_create); +EXPORT_SYMBOL(snd_card_new);  /* return non-zero if a card is already locked */  int snd_card_locked(int card) @@ -246,7 +280,7 @@ int snd_card_locked(int card)  	int locked;  	mutex_lock(&snd_card_mutex); -	locked = snd_cards_lock & (1 << card); +	locked = test_bit(card, snd_cards_lock);  	mutex_unlock(&snd_card_mutex);  	return locked;  } @@ -334,7 +368,7 @@ static const struct file_operations snd_shutdown_f_ops =   *   *  Disconnects all APIs from the file-operations (user space).   * - *  Returns zero, otherwise a negative error code. + *  Return: Zero, otherwise a negative error code.   *   *  Note: The current implementation replaces all active file->f_op with special   *        dummy file operations (they do nothing except release). @@ -342,7 +376,6 @@ static const struct file_operations snd_shutdown_f_ops =  int snd_card_disconnect(struct snd_card *card)  {  	struct snd_monitor_file *mfile; -	struct file *file;  	int err;  	if (!card) @@ -359,15 +392,13 @@ int snd_card_disconnect(struct snd_card *card)  	/* phase 1: disable fops (user space) operations for ALSA API */  	mutex_lock(&snd_card_mutex);  	snd_cards[card->number] = NULL; -	snd_cards_lock &= ~(1 << card->number); +	clear_bit(card->number, snd_cards_lock);  	mutex_unlock(&snd_card_mutex);  	/* phase 2: replace file->f_op with special dummy operations */  	spin_lock(&card->files_lock);  	list_for_each_entry(mfile, &card->files_list, list) { -		file = mfile->file; -  		/* it's critical part, use endless loop */  		/* we have no room to fail */  		mfile->disconnected_f_op = mfile->file->f_op; @@ -384,7 +415,7 @@ int snd_card_disconnect(struct snd_card *card)  	/* phase 3: notify all connected devices about disconnection */  	/* at this point, they cannot respond to any calls except release() */ -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS)  	if (snd_mixer_oss_notify_callback)  		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);  #endif @@ -392,12 +423,12 @@ int snd_card_disconnect(struct snd_card *card)  	/* notify all devices that we are disconnected */  	err = snd_device_disconnect_all(card);  	if (err < 0) -		snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); +		dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number);  	snd_info_card_disconnect(card); -	if (card->card_dev) { -		device_unregister(card->card_dev); -		card->card_dev = NULL; +	if (card->registered) { +		device_del(&card->card_dev); +		card->registered = false;  	}  #ifdef CONFIG_PM  	wake_up(&card->power_sleep); @@ -415,141 +446,149 @@ EXPORT_SYMBOL(snd_card_disconnect);   *  devices automatically.  That is, you don't have to release the devices   *  by yourself.   * - *  Returns zero. Frees all associated devices and frees the control + *  Return: Zero. Frees all associated devices and frees the control   *  interface associated to given soundcard.   */  static int snd_card_do_free(struct snd_card *card)  { -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS)  	if (snd_mixer_oss_notify_callback)  		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);  #endif -	if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { -		snd_printk(KERN_ERR "unable to free all devices (pre)\n"); -		/* Fatal, but this situation should never occur */ -	} -	if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { -		snd_printk(KERN_ERR "unable to free all devices (normal)\n"); -		/* Fatal, but this situation should never occur */ -	} -	if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { -		snd_printk(KERN_ERR "unable to free all devices (post)\n"); -		/* Fatal, but this situation should never occur */ -	} +	snd_device_free_all(card);  	if (card->private_free)  		card->private_free(card);  	snd_info_free_entry(card->proc_id);  	if (snd_info_card_free(card) < 0) { -		snd_printk(KERN_WARNING "unable to free card info\n"); +		dev_warn(card->dev, "unable to free card info\n");  		/* Not fatal error */  	} +	if (card->release_completion) +		complete(card->release_completion);  	kfree(card);  	return 0;  }  int snd_card_free_when_closed(struct snd_card *card)  { -	int free_now = 0;  	int ret = snd_card_disconnect(card);  	if (ret)  		return ret; - -	spin_lock(&card->files_lock); -	if (list_empty(&card->files_list)) -		free_now = 1; -	else -		card->free_on_last_close = 1; -	spin_unlock(&card->files_lock); - -	if (free_now) -		snd_card_do_free(card); +	put_device(&card->card_dev);  	return 0;  } -  EXPORT_SYMBOL(snd_card_free_when_closed);  int snd_card_free(struct snd_card *card)  { -	int ret = snd_card_disconnect(card); +	struct completion released; +	int ret; + +	init_completion(&released); +	card->release_completion = &released; +	ret = snd_card_free_when_closed(card);  	if (ret)  		return ret; -  	/* wait, until all devices are ready for the free operation */ -	wait_event(card->shutdown_sleep, list_empty(&card->files_list)); -	snd_card_do_free(card); +	wait_for_completion(&released);  	return 0;  } -  EXPORT_SYMBOL(snd_card_free); -static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid) +/* retrieve the last word of shortname or longname */ +static const char *retrieve_id_from_card_name(const char *name)  { -	int i, len, idx_flag = 0, loops = SNDRV_CARDS; -	const char *spos, *src; +	const char *spos = name; + +	while (*name) { +		if (isspace(*name) && isalnum(name[1])) +			spos = name + 1; +		name++; +	} +	return spos; +} + +/* return true if the given id string doesn't conflict any other card ids */ +static bool card_id_ok(struct snd_card *card, const char *id) +{ +	int i; +	if (!snd_info_check_reserved_words(id)) +		return false; +	for (i = 0; i < snd_ecards_limit; i++) { +		if (snd_cards[i] && snd_cards[i] != card && +		    !strcmp(snd_cards[i]->id, id)) +			return false; +	} +	return true; +} + +/* copy to card->id only with valid letters from nid */ +static void copy_valid_id_string(struct snd_card *card, const char *src, +				 const char *nid) +{ +	char *id = card->id; + +	while (*nid && !isalnum(*nid)) +		nid++; +	if (isdigit(*nid)) +		*id++ = isalpha(*src) ? *src : 'D'; +	while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) { +		if (isalnum(*nid)) +			*id++ = *nid; +		nid++; +	} +	*id = 0; +} + +/* Set card->id from the given string + * If the string conflicts with other ids, add a suffix to make it unique. + */ +static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, +				    const char *nid) +{ +	int len, loops; +	bool is_default = false;  	char *id; -	if (nid == NULL) { -		id = card->shortname; -		spos = src = id; -		while (*id != '\0') { -			if (*id == ' ') -				spos = id + 1; -			id++; -		} -	} else { -		spos = src = nid; -	} +	copy_valid_id_string(card, src, nid);  	id = card->id; -	while (*spos != '\0' && !isalnum(*spos)) -		spos++; -	if (isdigit(*spos)) -		*id++ = isalpha(src[0]) ? src[0] : 'D'; -	while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) { -		if (isalnum(*spos)) -			*id++ = *spos; -		spos++; + + again: +	/* use "Default" for obviously invalid strings +	 * ("card" conflicts with proc directories) +	 */ +	if (!*id || !strncmp(id, "card", 4)) { +		strcpy(id, "Default"); +		is_default = true;  	} -	*id = '\0'; -	id = card->id; -	 -	if (*id == '\0') -		strcpy(id, "default"); +	len = strlen(id); +	for (loops = 0; loops < SNDRV_CARDS; loops++) { +		char *spos; +		char sfxstr[5]; /* "_012" */ +		int sfxlen; -	while (1) { -	      	if (loops-- == 0) { -			snd_printk(KERN_ERR "unable to set card id (%s)\n", id); -      			strcpy(card->id, card->proc_root->name); -      			return; -      		} -	      	if (!snd_info_check_reserved_words(id)) -      			goto __change; -		for (i = 0; i < snd_ecards_limit; i++) { -			if (snd_cards[i] && !strcmp(snd_cards[i]->id, id)) -				goto __change; -		} -		break; - -	      __change: -		len = strlen(id); -		if (idx_flag) { -			if (id[len-1] != '9') -				id[len-1]++; -			else -				id[len-1] = 'A'; -		} else if ((size_t)len <= sizeof(card->id) - 3) { -			strcat(id, "_1"); -			idx_flag++; -		} else { -			spos = id + len - 2; -			if ((size_t)len <= sizeof(card->id) - 2) -				spos++; -			*(char *)spos++ = '_'; -			*(char *)spos++ = '1'; -			*(char *)spos++ = '\0'; -			idx_flag++; -		} +		if (card_id_ok(card, id)) +			return; /* OK */ + +		/* Add _XYZ suffix */ +		sprintf(sfxstr, "_%X", loops + 1); +		sfxlen = strlen(sfxstr); +		if (len + sfxlen >= sizeof(card->id)) +			spos = id + sizeof(card->id) - sfxlen - 1; +		else +			spos = id + len; +		strcpy(spos, sfxstr); +	} +	/* fallback to the default id */ +	if (!is_default) { +		*id = 0; +		goto again;  	} +	/* last resort... */ +	dev_err(card->dev, "unable to set card id (%s)\n", id); +	if (card->proc_root->name) +		strlcpy(card->id, card->proc_root->name, sizeof(card->id));  }  /** @@ -566,7 +605,7 @@ void snd_card_set_id(struct snd_card *card, const char *nid)  	if (card->id[0] != '\0')  		return;  	mutex_lock(&snd_card_mutex); -	snd_card_set_id_no_lock(card, nid); +	snd_card_set_id_no_lock(card, nid, nid);  	mutex_unlock(&snd_card_mutex);  }  EXPORT_SYMBOL(snd_card_set_id); @@ -575,15 +614,15 @@ static ssize_t  card_id_show_attr(struct device *dev,  		  struct device_attribute *attr, char *buf)  { -	struct snd_card *card = dev_get_drvdata(dev); -	return snprintf(buf, PAGE_SIZE, "%s\n", card ? card->id : "(null)"); +	struct snd_card *card = container_of(dev, struct snd_card, card_dev); +	return snprintf(buf, PAGE_SIZE, "%s\n", card->id);  }  static ssize_t  card_id_store_attr(struct device *dev, struct device_attribute *attr,  		   const char *buf, size_t count)  { -	struct snd_card *card = dev_get_drvdata(dev); +	struct snd_card *card = container_of(dev, struct snd_card, card_dev);  	char buf1[sizeof(card->id)];  	size_t copy = count > sizeof(card->id) - 1 ?  					sizeof(card->id) - 1 : count; @@ -598,40 +637,43 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr,  	memcpy(buf1, buf, copy);  	buf1[copy] = '\0';  	mutex_lock(&snd_card_mutex); -	if (!snd_info_check_reserved_words(buf1)) { -	     __exist: +	if (!card_id_ok(NULL, buf1)) {  		mutex_unlock(&snd_card_mutex);  		return -EEXIST;  	} -	for (idx = 0; idx < snd_ecards_limit; idx++) { -		if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) { -			if (card == snd_cards[idx]) -				goto __ok; -			else -				goto __exist; -		} -	}  	strcpy(card->id, buf1);  	snd_info_card_id_change(card); -__ok:  	mutex_unlock(&snd_card_mutex);  	return count;  } -static struct device_attribute card_id_attrs = -	__ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr); +static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr);  static ssize_t  card_number_show_attr(struct device *dev,  		     struct device_attribute *attr, char *buf)  { -	struct snd_card *card = dev_get_drvdata(dev); -	return snprintf(buf, PAGE_SIZE, "%i\n", card ? card->number : -1); +	struct snd_card *card = container_of(dev, struct snd_card, card_dev); +	return snprintf(buf, PAGE_SIZE, "%i\n", card->number);  } -static struct device_attribute card_number_attrs = -	__ATTR(number, S_IRUGO, card_number_show_attr, NULL); +static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL); + +static struct attribute *card_dev_attrs[] = { +	&dev_attr_id.attr, +	&dev_attr_number.attr, +	NULL +}; + +static struct attribute_group card_dev_attr_group = { +	.attrs	= card_dev_attrs, +}; + +static const struct attribute_group *card_dev_attr_groups[] = { +	&card_dev_attr_group, +	NULL +};  /**   *  snd_card_register - register the soundcard @@ -642,7 +684,7 @@ static struct device_attribute card_number_attrs =   *  external accesses.  Thus, you should call this function at the end   *  of the initialization of the card.   * - *  Returns zero otherwise a negative error code if the registrain failed. + *  Return: Zero otherwise a negative error code if the registration failed.   */  int snd_card_register(struct snd_card *card)  { @@ -651,12 +693,11 @@ int snd_card_register(struct snd_card *card)  	if (snd_BUG_ON(!card))  		return -EINVAL; -	if (!card->card_dev) { -		card->card_dev = device_create(sound_class, card->dev, -					       MKDEV(0, 0), card, -					       "card%i", card->number); -		if (IS_ERR(card->card_dev)) -			card->card_dev = NULL; +	if (!card->registered) { +		err = device_add(&card->card_dev); +		if (err < 0) +			return err; +		card->registered = true;  	}  	if ((err = snd_device_register_all(card)) < 0) @@ -667,23 +708,25 @@ int snd_card_register(struct snd_card *card)  		mutex_unlock(&snd_card_mutex);  		return 0;  	} -	snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id); +	if (*card->id) { +		/* make a unique id name from the given string */ +		char tmpid[sizeof(card->id)]; +		memcpy(tmpid, card->id, sizeof(card->id)); +		snd_card_set_id_no_lock(card, tmpid, tmpid); +	} else { +		/* create an id from either shortname or longname */ +		const char *src; +		src = *card->shortname ? card->shortname : card->longname; +		snd_card_set_id_no_lock(card, src, +					retrieve_id_from_card_name(src)); +	}  	snd_cards[card->number] = card;  	mutex_unlock(&snd_card_mutex);  	init_info_for_card(card); -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS)  	if (snd_mixer_oss_notify_callback)  		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);  #endif -	if (card->card_dev) { -		err = device_create_file(card->card_dev, &card_id_attrs); -		if (err < 0) -			return err; -		err = device_create_file(card->card_dev, &card_number_attrs); -		if (err < 0) -			return err; -	} -  	return 0;  } @@ -803,7 +846,7 @@ int __exit snd_card_info_done(void)   *  This function adds the component id string to the supported list.   *  The component can be referred from the alsa-lib.   * - *  Returns zero otherwise a negative error code. + *  Return: Zero otherwise a negative error code.   */  int snd_component_add(struct snd_card *card, const char *component) @@ -837,7 +880,7 @@ EXPORT_SYMBOL(snd_component_add);   *  This linked-list is used to keep tracking the connection state,   *  and to avoid the release of busy resources by hotplug.   * - *  Returns zero or a negative error code. + *  Return: zero or a negative error code.   */  int snd_card_file_add(struct snd_card *card, struct file *file)  { @@ -848,6 +891,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file)  		return -ENOMEM;  	mfile->file = file;  	mfile->disconnected_f_op = NULL; +	INIT_LIST_HEAD(&mfile->shutdown_list);  	spin_lock(&card->files_lock);  	if (card->shutdown) {  		spin_unlock(&card->files_lock); @@ -855,6 +899,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file)  		return -ENODEV;  	}  	list_add(&mfile->list, &card->files_list); +	get_device(&card->card_dev);  	spin_unlock(&card->files_lock);  	return 0;  } @@ -872,36 +917,32 @@ EXPORT_SYMBOL(snd_card_file_add);   *  called beforehand, it processes the pending release of   *  resources.   * - *  Returns zero or a negative error code. + *  Return: Zero or a negative error code.   */  int snd_card_file_remove(struct snd_card *card, struct file *file)  {  	struct snd_monitor_file *mfile, *found = NULL; -	int last_close = 0;  	spin_lock(&card->files_lock);  	list_for_each_entry(mfile, &card->files_list, list) {  		if (mfile->file == file) {  			list_del(&mfile->list); +			spin_lock(&shutdown_lock); +			list_del(&mfile->shutdown_list); +			spin_unlock(&shutdown_lock);  			if (mfile->disconnected_f_op)  				fops_put(mfile->disconnected_f_op);  			found = mfile;  			break;  		}  	} -	if (list_empty(&card->files_list)) -		last_close = 1;  	spin_unlock(&card->files_lock); -	if (last_close) { -		wake_up(&card->shutdown_sleep); -		if (card->free_on_last_close) -			snd_card_do_free(card); -	}  	if (!found) { -		snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); +		dev_err(card->dev, "card file remove problem (%p)\n", file);  		return -ENOENT;  	}  	kfree(found); +	put_device(&card->card_dev);  	return 0;  } @@ -915,6 +956,8 @@ EXPORT_SYMBOL(snd_card_file_remove);   *   *  Waits until the power-state is changed.   * + *  Return: Zero if successful, or a negative error code. + *   *  Note: the power lock must be active before call.   */  int snd_power_wait(struct snd_card *card, unsigned int power_state)  | 
