diff options
Diffstat (limited to 'sound/core/control.c')
| -rw-r--r-- | sound/core/control.c | 95 | 
1 files changed, 59 insertions, 36 deletions
diff --git a/sound/core/control.c b/sound/core/control.c index d8aa206e8bd..f0b0e14497a 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -151,7 +151,7 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,  	if (snd_BUG_ON(!card || !id))  		return;  	read_lock(&card->ctl_files_rwlock); -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS)  	card->mixer_oss_change_count++;  #endif  	list_for_each_entry(ctl, &card->ctl_files, list) { @@ -170,7 +170,7 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,  			ev->mask = mask;  			list_add_tail(&ev->list, &ctl->events);  		} else { -			snd_printk(KERN_ERR "No memory available to allocate event\n"); +			dev_err(card->dev, "No memory available to allocate event\n");  		}  	_found:  		wake_up(&ctl->change_sleep); @@ -206,7 +206,7 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,  	kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);  	if (kctl == NULL) { -		snd_printk(KERN_ERR "Cannot allocate control instance\n"); +		pr_err("ALSA: Cannot allocate control instance\n");  		return NULL;  	}  	*kctl = *control; @@ -241,9 +241,8 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,  	if (ncontrol->name) {  		strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));  		if (strcmp(ncontrol->name, kctl.id.name) != 0) -			snd_printk(KERN_WARNING -				   "Control name '%s' truncated to '%s'\n", -				   ncontrol->name, kctl.id.name); +			pr_warn("ALSA: Control name '%s' truncated to '%s'\n", +				ncontrol->name, kctl.id.name);  	}  	kctl.id.index = ncontrol->index;  	kctl.count = ncontrol->count ? ncontrol->count : 1; @@ -289,6 +288,10 @@ static bool snd_ctl_remove_numid_conflict(struct snd_card *card,  {  	struct snd_kcontrol *kctl; +	/* Make sure that the ids assigned to the control do not wrap around */ +	if (card->last_numid >= UINT_MAX - count) +		card->last_numid = 0; +  	list_for_each_entry(kctl, &card->controls, list) {  		if (kctl->id.numid < card->last_numid + 1 + count &&  		    kctl->id.numid + kctl->count > card->last_numid + 1) { @@ -306,7 +309,7 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)  	while (snd_ctl_remove_numid_conflict(card, count)) {  		if (--iter == 0) {  			/* this situation is very unlikely */ -			snd_printk(KERN_ERR "unable to allocate new control numid\n"); +			dev_err(card->dev, "unable to allocate new control numid\n");  			return -ENOMEM;  		}  	} @@ -331,6 +334,7 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)  {  	struct snd_ctl_elem_id id;  	unsigned int idx; +	unsigned int count;  	int err = -EINVAL;  	if (! kcontrol) @@ -338,10 +342,13 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)  	if (snd_BUG_ON(!card || !kcontrol->info))  		goto error;  	id = kcontrol->id; +	if (id.index > UINT_MAX - kcontrol->count) +		goto error; +  	down_write(&card->controls_rwsem);  	if (snd_ctl_find_id(card, &id)) {  		up_write(&card->controls_rwsem); -		snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n", +		dev_err(card->dev, "control %i:%i:%i:%s:%i is already present\n",  					id.iface,  					id.device,  					id.subdevice, @@ -359,8 +366,9 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)  	card->controls_count += kcontrol->count;  	kcontrol->id.numid = card->last_numid + 1;  	card->last_numid += kcontrol->count; +	count = kcontrol->count;  	up_write(&card->controls_rwsem); -	for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) +	for (idx = 0; idx < count; idx++, id.index++, id.numid++)  		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);  	return 0; @@ -389,6 +397,7 @@ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,  		    bool add_on_replace)  {  	struct snd_ctl_elem_id id; +	unsigned int count;  	unsigned int idx;  	struct snd_kcontrol *old;  	int ret; @@ -424,8 +433,9 @@ add:  	card->controls_count += kcontrol->count;  	kcontrol->id.numid = card->last_numid + 1;  	card->last_numid += kcontrol->count; +	count = kcontrol->count;  	up_write(&card->controls_rwsem); -	for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) +	for (idx = 0; idx < count; idx++, id.index++, id.numid++)  		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);  	return 0; @@ -898,9 +908,9 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,  			result = kctl->put(kctl, control);  		}  		if (result > 0) { +			struct snd_ctl_elem_id id = control->id;  			up_read(&card->controls_rwsem); -			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, -				       &control->id); +			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id);  			return 0;  		}  	} @@ -992,6 +1002,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,  struct user_element {  	struct snd_ctl_elem_info info; +	struct snd_card *card;  	void *elem_data;		/* element data */  	unsigned long elem_data_size;	/* size of element data in bytes */  	void *tlv_data;			/* TLV data */ @@ -1035,7 +1046,9 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,  {  	struct user_element *ue = kcontrol->private_data; +	mutex_lock(&ue->card->user_ctl_lock);  	memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size); +	mutex_unlock(&ue->card->user_ctl_lock);  	return 0;  } @@ -1044,10 +1057,12 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,  {  	int change;  	struct user_element *ue = kcontrol->private_data; -	 + +	mutex_lock(&ue->card->user_ctl_lock);  	change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;  	if (change)  		memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); +	mutex_unlock(&ue->card->user_ctl_lock);  	return change;  } @@ -1067,19 +1082,32 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,  		new_data = memdup_user(tlv, size);  		if (IS_ERR(new_data))  			return PTR_ERR(new_data); +		mutex_lock(&ue->card->user_ctl_lock);  		change = ue->tlv_data_size != size;  		if (!change)  			change = memcmp(ue->tlv_data, new_data, size);  		kfree(ue->tlv_data);  		ue->tlv_data = new_data;  		ue->tlv_data_size = size; +		mutex_unlock(&ue->card->user_ctl_lock);  	} else { -		if (! ue->tlv_data_size || ! ue->tlv_data) -			return -ENXIO; -		if (size < ue->tlv_data_size) -			return -ENOSPC; +		int ret = 0; + +		mutex_lock(&ue->card->user_ctl_lock); +		if (!ue->tlv_data_size || !ue->tlv_data) { +			ret = -ENXIO; +			goto err_unlock; +		} +		if (size < ue->tlv_data_size) { +			ret = -ENOSPC; +			goto err_unlock; +		}  		if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size)) -			return -EFAULT; +			ret = -EFAULT; +err_unlock: +		mutex_unlock(&ue->card->user_ctl_lock); +		if (ret) +			return ret;  	}  	return change;  } @@ -1137,8 +1165,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,  	struct user_element *ue;  	int idx, err; -	if (!replace && card->user_ctl_count >= MAX_USER_CONTROLS) -		return -ENOMEM;  	if (info->count < 1)  		return -EINVAL;  	access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : @@ -1147,21 +1173,16 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,  				 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));  	info->id.numid = 0;  	memset(&kctl, 0, sizeof(kctl)); -	down_write(&card->controls_rwsem); -	_kctl = snd_ctl_find_id(card, &info->id); -	err = 0; -	if (_kctl) { -		if (replace) -			err = snd_ctl_remove(card, _kctl); -		else -			err = -EBUSY; -	} else { -		if (replace) -			err = -ENOENT; + +	if (replace) { +		err = snd_ctl_remove_user_ctl(file, &info->id); +		if (err) +			return err;  	} -	up_write(&card->controls_rwsem); -	if (err < 0) -		return err; + +	if (card->user_ctl_count >= MAX_USER_CONTROLS) +		return -ENOMEM; +  	memcpy(&kctl.id, &info->id, sizeof(info->id));  	kctl.count = info->owner ? info->owner : 1;  	access |= SNDRV_CTL_ELEM_ACCESS_USER; @@ -1211,6 +1232,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,  	ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);  	if (ue == NULL)  		return -ENOMEM; +	ue->card = card;  	ue->info = *info;  	ue->info.access = 0;  	ue->elem_data = (char *)ue + sizeof(*ue); @@ -1322,8 +1344,9 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,  		}  		err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);  		if (err > 0) { +			struct snd_ctl_elem_id id = kctl->id;  			up_read(&card->controls_rwsem); -			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id); +			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &id);  			return 0;  		}  	} else { @@ -1406,7 +1429,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg  		}  	}  	up_read(&snd_ioctl_rwsem); -	snd_printdd("unknown ioctl = 0x%x\n", cmd); +	dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd);  	return -ENOTTY;  }  | 
