1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * The Industrial I/O core, software IIO devices functions |
4 | * |
5 | * Copyright (c) 2016 Intel Corporation |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/init.h> |
10 | #include <linux/kmod.h> |
11 | #include <linux/list.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #include <linux/iio/sw_device.h> |
15 | #include <linux/iio/configfs.h> |
16 | #include <linux/configfs.h> |
17 | |
18 | static struct config_group *iio_devices_group; |
19 | static const struct config_item_type iio_device_type_group_type; |
20 | |
21 | static const struct config_item_type iio_devices_group_type = { |
22 | .ct_owner = THIS_MODULE, |
23 | }; |
24 | |
25 | static LIST_HEAD(iio_device_types_list); |
26 | static DEFINE_MUTEX(iio_device_types_lock); |
27 | |
28 | static |
29 | struct iio_sw_device_type *__iio_find_sw_device_type(const char *name, |
30 | unsigned int len) |
31 | { |
32 | struct iio_sw_device_type *d = NULL, *iter; |
33 | |
34 | list_for_each_entry(iter, &iio_device_types_list, list) |
35 | if (!strcmp(iter->name, name)) { |
36 | d = iter; |
37 | break; |
38 | } |
39 | |
40 | return d; |
41 | } |
42 | |
43 | int iio_register_sw_device_type(struct iio_sw_device_type *d) |
44 | { |
45 | struct iio_sw_device_type *iter; |
46 | int ret = 0; |
47 | |
48 | mutex_lock(&iio_device_types_lock); |
49 | iter = __iio_find_sw_device_type(name: d->name, strlen(d->name)); |
50 | if (iter) |
51 | ret = -EBUSY; |
52 | else |
53 | list_add_tail(new: &d->list, head: &iio_device_types_list); |
54 | mutex_unlock(lock: &iio_device_types_lock); |
55 | |
56 | if (ret) |
57 | return ret; |
58 | |
59 | d->group = configfs_register_default_group(parent_group: iio_devices_group, name: d->name, |
60 | item_type: &iio_device_type_group_type); |
61 | if (IS_ERR(ptr: d->group)) |
62 | ret = PTR_ERR(ptr: d->group); |
63 | |
64 | return ret; |
65 | } |
66 | EXPORT_SYMBOL(iio_register_sw_device_type); |
67 | |
68 | void iio_unregister_sw_device_type(struct iio_sw_device_type *dt) |
69 | { |
70 | struct iio_sw_device_type *iter; |
71 | |
72 | mutex_lock(&iio_device_types_lock); |
73 | iter = __iio_find_sw_device_type(name: dt->name, strlen(dt->name)); |
74 | if (iter) |
75 | list_del(entry: &dt->list); |
76 | mutex_unlock(lock: &iio_device_types_lock); |
77 | |
78 | configfs_unregister_default_group(group: dt->group); |
79 | } |
80 | EXPORT_SYMBOL(iio_unregister_sw_device_type); |
81 | |
82 | static |
83 | struct iio_sw_device_type *iio_get_sw_device_type(const char *name) |
84 | { |
85 | struct iio_sw_device_type *dt; |
86 | |
87 | mutex_lock(&iio_device_types_lock); |
88 | dt = __iio_find_sw_device_type(name, strlen(name)); |
89 | if (dt && !try_module_get(module: dt->owner)) |
90 | dt = NULL; |
91 | mutex_unlock(lock: &iio_device_types_lock); |
92 | |
93 | return dt; |
94 | } |
95 | |
96 | struct iio_sw_device *iio_sw_device_create(const char *type, const char *name) |
97 | { |
98 | struct iio_sw_device *d; |
99 | struct iio_sw_device_type *dt; |
100 | |
101 | dt = iio_get_sw_device_type(name: type); |
102 | if (!dt) { |
103 | pr_err("Invalid device type: %s\n" , type); |
104 | return ERR_PTR(error: -EINVAL); |
105 | } |
106 | d = dt->ops->probe(name); |
107 | if (IS_ERR(ptr: d)) |
108 | goto out_module_put; |
109 | |
110 | d->device_type = dt; |
111 | |
112 | return d; |
113 | out_module_put: |
114 | module_put(module: dt->owner); |
115 | return d; |
116 | } |
117 | EXPORT_SYMBOL(iio_sw_device_create); |
118 | |
119 | void iio_sw_device_destroy(struct iio_sw_device *d) |
120 | { |
121 | struct iio_sw_device_type *dt = d->device_type; |
122 | |
123 | dt->ops->remove(d); |
124 | module_put(module: dt->owner); |
125 | } |
126 | EXPORT_SYMBOL(iio_sw_device_destroy); |
127 | |
128 | static struct config_group *device_make_group(struct config_group *group, |
129 | const char *name) |
130 | { |
131 | struct iio_sw_device *d; |
132 | |
133 | d = iio_sw_device_create(group->cg_item.ci_name, name); |
134 | if (IS_ERR(ptr: d)) |
135 | return ERR_CAST(ptr: d); |
136 | |
137 | config_item_set_name(&d->group.cg_item, "%s" , name); |
138 | |
139 | return &d->group; |
140 | } |
141 | |
142 | static void device_drop_group(struct config_group *group, |
143 | struct config_item *item) |
144 | { |
145 | struct iio_sw_device *d = to_iio_sw_device(item); |
146 | |
147 | iio_sw_device_destroy(d); |
148 | config_item_put(item); |
149 | } |
150 | |
151 | static struct configfs_group_operations device_ops = { |
152 | .make_group = &device_make_group, |
153 | .drop_item = &device_drop_group, |
154 | }; |
155 | |
156 | static const struct config_item_type iio_device_type_group_type = { |
157 | .ct_group_ops = &device_ops, |
158 | .ct_owner = THIS_MODULE, |
159 | }; |
160 | |
161 | static int __init iio_sw_device_init(void) |
162 | { |
163 | iio_devices_group = |
164 | configfs_register_default_group(parent_group: &iio_configfs_subsys.su_group, |
165 | name: "devices" , |
166 | item_type: &iio_devices_group_type); |
167 | return PTR_ERR_OR_ZERO(ptr: iio_devices_group); |
168 | } |
169 | module_init(iio_sw_device_init); |
170 | |
171 | static void __exit iio_sw_device_exit(void) |
172 | { |
173 | configfs_unregister_default_group(group: iio_devices_group); |
174 | } |
175 | module_exit(iio_sw_device_exit); |
176 | |
177 | MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>" ); |
178 | MODULE_DESCRIPTION("Industrial I/O software devices support" ); |
179 | MODULE_LICENSE("GPL v2" ); |
180 | |