18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * The Industrial I/O core, software IIO devices functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2016 Intel Corporation
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/kmod.h>
118c2ecf20Sopenharmony_ci#include <linux/list.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/iio/sw_device.h>
158c2ecf20Sopenharmony_ci#include <linux/iio/configfs.h>
168c2ecf20Sopenharmony_ci#include <linux/configfs.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic struct config_group *iio_devices_group;
198c2ecf20Sopenharmony_cistatic const struct config_item_type iio_device_type_group_type;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic const struct config_item_type iio_devices_group_type = {
228c2ecf20Sopenharmony_ci	.ct_owner = THIS_MODULE,
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic LIST_HEAD(iio_device_types_list);
268c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(iio_device_types_lock);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic
298c2ecf20Sopenharmony_cistruct iio_sw_device_type *__iio_find_sw_device_type(const char *name,
308c2ecf20Sopenharmony_ci						     unsigned len)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct iio_sw_device_type *d = NULL, *iter;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &iio_device_types_list, list)
358c2ecf20Sopenharmony_ci		if (!strcmp(iter->name, name)) {
368c2ecf20Sopenharmony_ci			d = iter;
378c2ecf20Sopenharmony_ci			break;
388c2ecf20Sopenharmony_ci		}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	return d;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ciint iio_register_sw_device_type(struct iio_sw_device_type *d)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct iio_sw_device_type *iter;
468c2ecf20Sopenharmony_ci	int ret = 0;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	mutex_lock(&iio_device_types_lock);
498c2ecf20Sopenharmony_ci	iter = __iio_find_sw_device_type(d->name, strlen(d->name));
508c2ecf20Sopenharmony_ci	if (iter)
518c2ecf20Sopenharmony_ci		ret = -EBUSY;
528c2ecf20Sopenharmony_ci	else
538c2ecf20Sopenharmony_ci		list_add_tail(&d->list, &iio_device_types_list);
548c2ecf20Sopenharmony_ci	mutex_unlock(&iio_device_types_lock);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (ret)
578c2ecf20Sopenharmony_ci		return ret;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	d->group = configfs_register_default_group(iio_devices_group, d->name,
608c2ecf20Sopenharmony_ci						&iio_device_type_group_type);
618c2ecf20Sopenharmony_ci	if (IS_ERR(d->group))
628c2ecf20Sopenharmony_ci		ret = PTR_ERR(d->group);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	return ret;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iio_register_sw_device_type);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_civoid iio_unregister_sw_device_type(struct iio_sw_device_type *dt)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct iio_sw_device_type *iter;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	mutex_lock(&iio_device_types_lock);
738c2ecf20Sopenharmony_ci	iter = __iio_find_sw_device_type(dt->name, strlen(dt->name));
748c2ecf20Sopenharmony_ci	if (iter)
758c2ecf20Sopenharmony_ci		list_del(&dt->list);
768c2ecf20Sopenharmony_ci	mutex_unlock(&iio_device_types_lock);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	configfs_unregister_default_group(dt->group);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iio_unregister_sw_device_type);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic
838c2ecf20Sopenharmony_cistruct iio_sw_device_type *iio_get_sw_device_type(const char *name)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct iio_sw_device_type *dt;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	mutex_lock(&iio_device_types_lock);
888c2ecf20Sopenharmony_ci	dt = __iio_find_sw_device_type(name, strlen(name));
898c2ecf20Sopenharmony_ci	if (dt && !try_module_get(dt->owner))
908c2ecf20Sopenharmony_ci		dt = NULL;
918c2ecf20Sopenharmony_ci	mutex_unlock(&iio_device_types_lock);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return dt;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct iio_sw_device *iio_sw_device_create(const char *type, const char *name)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct iio_sw_device *d;
998c2ecf20Sopenharmony_ci	struct iio_sw_device_type *dt;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	dt = iio_get_sw_device_type(type);
1028c2ecf20Sopenharmony_ci	if (!dt) {
1038c2ecf20Sopenharmony_ci		pr_err("Invalid device type: %s\n", type);
1048c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	d = dt->ops->probe(name);
1078c2ecf20Sopenharmony_ci	if (IS_ERR(d))
1088c2ecf20Sopenharmony_ci		goto out_module_put;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	d->device_type = dt;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return d;
1138c2ecf20Sopenharmony_ciout_module_put:
1148c2ecf20Sopenharmony_ci	module_put(dt->owner);
1158c2ecf20Sopenharmony_ci	return d;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iio_sw_device_create);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_civoid iio_sw_device_destroy(struct iio_sw_device *d)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct iio_sw_device_type *dt = d->device_type;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	dt->ops->remove(d);
1248c2ecf20Sopenharmony_ci	module_put(dt->owner);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iio_sw_device_destroy);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic struct config_group *device_make_group(struct config_group *group,
1298c2ecf20Sopenharmony_ci					      const char *name)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct iio_sw_device *d;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	d = iio_sw_device_create(group->cg_item.ci_name, name);
1348c2ecf20Sopenharmony_ci	if (IS_ERR(d))
1358c2ecf20Sopenharmony_ci		return ERR_CAST(d);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	config_item_set_name(&d->group.cg_item, "%s", name);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return &d->group;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic void device_drop_group(struct config_group *group,
1438c2ecf20Sopenharmony_ci			      struct config_item *item)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct iio_sw_device *d = to_iio_sw_device(item);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	iio_sw_device_destroy(d);
1488c2ecf20Sopenharmony_ci	config_item_put(item);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic struct configfs_group_operations device_ops = {
1528c2ecf20Sopenharmony_ci	.make_group	= &device_make_group,
1538c2ecf20Sopenharmony_ci	.drop_item	= &device_drop_group,
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic const struct config_item_type iio_device_type_group_type = {
1578c2ecf20Sopenharmony_ci	.ct_group_ops = &device_ops,
1588c2ecf20Sopenharmony_ci	.ct_owner       = THIS_MODULE,
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic int __init iio_sw_device_init(void)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	iio_devices_group =
1648c2ecf20Sopenharmony_ci		configfs_register_default_group(&iio_configfs_subsys.su_group,
1658c2ecf20Sopenharmony_ci						"devices",
1668c2ecf20Sopenharmony_ci						&iio_devices_group_type);
1678c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(iio_devices_group);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_cimodule_init(iio_sw_device_init);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic void __exit iio_sw_device_exit(void)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	configfs_unregister_default_group(iio_devices_group);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_cimodule_exit(iio_sw_device_exit);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
1788c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Industrial I/O software devices support");
1798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
180