Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
0f3a8c3f DB |
2 | /* |
3 | * The Industrial I/O core, software IIO devices functions | |
4 | * | |
5 | * Copyright (c) 2016 Intel Corporation | |
0f3a8c3f DB |
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; | |
612a462a | 19 | static const struct config_item_type iio_device_type_group_type; |
0f3a8c3f | 20 | |
612a462a | 21 | static const struct config_item_type iio_devices_group_type = { |
0f3a8c3f DB |
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 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(d->name, strlen(d->name)); | |
50 | if (iter) | |
51 | ret = -EBUSY; | |
52 | else | |
53 | list_add_tail(&d->list, &iio_device_types_list); | |
54 | mutex_unlock(&iio_device_types_lock); | |
55 | ||
56 | if (ret) | |
57 | return ret; | |
58 | ||
59 | d->group = configfs_register_default_group(iio_devices_group, d->name, | |
60 | &iio_device_type_group_type); | |
61 | if (IS_ERR(d->group)) | |
62 | ret = PTR_ERR(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(dt->name, strlen(dt->name)); | |
74 | if (iter) | |
75 | list_del(&dt->list); | |
76 | mutex_unlock(&iio_device_types_lock); | |
77 | ||
78 | configfs_unregister_default_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(dt->owner)) | |
90 | dt = NULL; | |
91 | mutex_unlock(&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(type); | |
102 | if (!dt) { | |
103 | pr_err("Invalid device type: %s\n", type); | |
104 | return ERR_PTR(-EINVAL); | |
105 | } | |
106 | d = dt->ops->probe(name); | |
107 | if (IS_ERR(d)) | |
108 | goto out_module_put; | |
109 | ||
110 | d->device_type = dt; | |
111 | ||
112 | return d; | |
113 | out_module_put: | |
114 | module_put(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(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(d)) | |
135 | return ERR_CAST(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 | ||
612a462a | 156 | static const struct config_item_type iio_device_type_group_type = { |
0f3a8c3f DB |
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(&iio_configfs_subsys.su_group, | |
165 | "devices", | |
166 | &iio_devices_group_type); | |
167 | return PTR_ERR_OR_ZERO(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(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"); |