diff options
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-async.c')
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-async.c | 191 | 
1 files changed, 149 insertions, 42 deletions
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index f09d354b96a0..7925875d09b7 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -365,16 +365,26 @@ v4l2_async_notifier_has_async_subdev(struct v4l2_async_notifier *notifier,  				     struct v4l2_async_subdev *asd,  				     unsigned int this_index)  { +	struct v4l2_async_subdev *asd_y;  	unsigned int j;  	lockdep_assert_held(&list_lock);  	/* Check that an asd is not being added more than once. */ -	for (j = 0; j < this_index; j++) { -		struct v4l2_async_subdev *asd_y = notifier->subdevs[j]; - -		if (asd_equal(asd, asd_y)) -			return true; +	if (notifier->subdevs) { +		for (j = 0; j < this_index; j++) { +			asd_y = notifier->subdevs[j]; +			if (asd_equal(asd, asd_y)) +				return true; +		} +	} else { +		j = 0; +		list_for_each_entry(asd_y, ¬ifier->asd_list, asd_list) { +			if (j++ >= this_index) +				break; +			if (asd_equal(asd, asd_y)) +				return true; +		}  	}  	/* Check that an asd does not exist in other notifiers. */ @@ -385,10 +395,48 @@ v4l2_async_notifier_has_async_subdev(struct v4l2_async_notifier *notifier,  	return false;  } -static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) +static int v4l2_async_notifier_asd_valid(struct v4l2_async_notifier *notifier, +					 struct v4l2_async_subdev *asd, +					 unsigned int this_index)  {  	struct device *dev =  		notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL; + +	if (!asd) +		return -EINVAL; + +	switch (asd->match_type) { +	case V4L2_ASYNC_MATCH_CUSTOM: +	case V4L2_ASYNC_MATCH_DEVNAME: +	case V4L2_ASYNC_MATCH_I2C: +	case V4L2_ASYNC_MATCH_FWNODE: +		if (v4l2_async_notifier_has_async_subdev(notifier, asd, +							 this_index)) { +			dev_dbg(dev, "subdev descriptor already listed in this or other notifiers\n"); +			return -EEXIST; +		} +		break; +	default: +		dev_err(dev, "Invalid match type %u on %p\n", +			asd->match_type, asd); +		return -EINVAL; +	} + +	return 0; +} + +void v4l2_async_notifier_init(struct v4l2_async_notifier *notifier) +{ +	mutex_lock(&list_lock); + +	INIT_LIST_HEAD(¬ifier->asd_list); + +	mutex_unlock(&list_lock); +} +EXPORT_SYMBOL(v4l2_async_notifier_init); + +static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) +{  	struct v4l2_async_subdev *asd;  	int ret;  	int i; @@ -401,29 +449,25 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)  	mutex_lock(&list_lock); -	for (i = 0; i < notifier->num_subdevs; i++) { -		asd = notifier->subdevs[i]; +	if (notifier->subdevs) { +		for (i = 0; i < notifier->num_subdevs; i++) { +			asd = notifier->subdevs[i]; -		switch (asd->match_type) { -		case V4L2_ASYNC_MATCH_CUSTOM: -		case V4L2_ASYNC_MATCH_DEVNAME: -		case V4L2_ASYNC_MATCH_I2C: -		case V4L2_ASYNC_MATCH_FWNODE: -			if (v4l2_async_notifier_has_async_subdev(notifier, -								 asd, i)) { -				dev_err(dev, -					"subdev descriptor already listed in this or other notifiers\n"); -				ret = -EEXIST; +			ret = v4l2_async_notifier_asd_valid(notifier, asd, i); +			if (ret)  				goto err_unlock; -			} -			break; -		default: -			dev_err(dev, "Invalid match type %u on %p\n", -				asd->match_type, asd); -			ret = -EINVAL; -			goto err_unlock; + +			list_add_tail(&asd->list, ¬ifier->waiting); +		} +	} else { +		i = 0; +		list_for_each_entry(asd, ¬ifier->asd_list, asd_list) { +			ret = v4l2_async_notifier_asd_valid(notifier, asd, i++); +			if (ret) +				goto err_unlock; + +			list_add_tail(&asd->list, ¬ifier->waiting);  		} -		list_add_tail(&asd->list, ¬ifier->waiting);  	}  	ret = v4l2_async_notifier_try_all_subdevs(notifier); @@ -513,36 +557,99 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)  }  EXPORT_SYMBOL(v4l2_async_notifier_unregister); -void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) +static void __v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier)  { +	struct v4l2_async_subdev *asd, *tmp;  	unsigned int i; -	if (!notifier || !notifier->max_subdevs) +	if (!notifier)  		return; -	for (i = 0; i < notifier->num_subdevs; i++) { -		struct v4l2_async_subdev *asd = notifier->subdevs[i]; +	if (notifier->subdevs) { +		if (!notifier->max_subdevs) +			return; -		switch (asd->match_type) { -		case V4L2_ASYNC_MATCH_FWNODE: -			fwnode_handle_put(asd->match.fwnode); -			break; -		default: -			WARN_ON_ONCE(true); -			break; +		for (i = 0; i < notifier->num_subdevs; i++) { +			asd = notifier->subdevs[i]; + +			switch (asd->match_type) { +			case V4L2_ASYNC_MATCH_FWNODE: +				fwnode_handle_put(asd->match.fwnode); +				break; +			default: +				break; +			} + +			kfree(asd);  		} -		kfree(asd); +		notifier->max_subdevs = 0; +		kvfree(notifier->subdevs); +		notifier->subdevs = NULL; +	} else { +		list_for_each_entry_safe(asd, tmp, +					 ¬ifier->asd_list, asd_list) { +			switch (asd->match_type) { +			case V4L2_ASYNC_MATCH_FWNODE: +				fwnode_handle_put(asd->match.fwnode); +				break; +			default: +				break; +			} + +			list_del(&asd->asd_list); +			kfree(asd); +		}  	} -	notifier->max_subdevs = 0;  	notifier->num_subdevs = 0; +} + +void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) +{ +	mutex_lock(&list_lock); + +	__v4l2_async_notifier_cleanup(notifier); -	kvfree(notifier->subdevs); -	notifier->subdevs = NULL; +	mutex_unlock(&list_lock);  }  EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup); +int v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, +				   struct v4l2_async_subdev *asd) +{ +	int ret; + +	mutex_lock(&list_lock); + +	if (notifier->num_subdevs >= V4L2_MAX_SUBDEVS) { +		ret = -EINVAL; +		goto unlock; +	} + +	/* +	 * If caller uses this function, it cannot also allocate and +	 * place asd's in the notifier->subdevs array. +	 */ +	if (WARN_ON(notifier->subdevs)) { +		ret = -EINVAL; +		goto unlock; +	} + +	ret = v4l2_async_notifier_asd_valid(notifier, asd, +					    notifier->num_subdevs); +	if (ret) +		goto unlock; + +	list_add_tail(&asd->asd_list, ¬ifier->asd_list); +	notifier->num_subdevs++; + +unlock: +	mutex_unlock(&list_lock); +	return ret; +} +EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_subdev); +  int v4l2_async_register_subdev(struct v4l2_subdev *sd)  {  	struct v4l2_async_notifier *subdev_notifier; @@ -616,7 +723,7 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)  	mutex_lock(&list_lock);  	__v4l2_async_notifier_unregister(sd->subdev_notifier); -	v4l2_async_notifier_cleanup(sd->subdev_notifier); +	__v4l2_async_notifier_cleanup(sd->subdev_notifier);  	kfree(sd->subdev_notifier);  	sd->subdev_notifier = NULL;  | 
