GNU Linux-libre 6.8.7-gnu
[releases.git] / drivers / staging / fieldbus / dev_core.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Fieldbus Device Driver Core
4  *
5  */
6
7 #include <linux/mutex.h>
8 #include <linux/module.h>
9 #include <linux/device.h>
10 #include <linux/idr.h>
11 #include <linux/fs.h>
12 #include <linux/slab.h>
13 #include <linux/poll.h>
14
15 /* move to <linux/fieldbus_dev.h> when taking this out of staging */
16 #include "fieldbus_dev.h"
17
18 /* Maximum number of fieldbus devices */
19 #define MAX_FIELDBUSES          32
20
21 /* the dev_t structure to store the dynamically allocated fieldbus devices */
22 static dev_t fieldbus_devt;
23 static DEFINE_IDA(fieldbus_ida);
24 static DEFINE_MUTEX(fieldbus_mtx);
25
26 static ssize_t online_show(struct device *dev, struct device_attribute *attr,
27                            char *buf)
28 {
29         struct fieldbus_dev *fb = dev_get_drvdata(dev);
30
31         return sysfs_emit(buf, "%d\n", !!fb->online);
32 }
33 static DEVICE_ATTR_RO(online);
34
35 static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
36                             char *buf)
37 {
38         struct fieldbus_dev *fb = dev_get_drvdata(dev);
39
40         if (!fb->enable_get)
41                 return -EINVAL;
42         return sysfs_emit(buf, "%d\n", !!fb->enable_get(fb));
43 }
44
45 static ssize_t enabled_store(struct device *dev, struct device_attribute *attr,
46                              const char *buf, size_t n)
47 {
48         struct fieldbus_dev *fb = dev_get_drvdata(dev);
49         bool value;
50         int ret;
51
52         if (!fb->simple_enable_set)
53                 return -ENOTSUPP;
54         ret = kstrtobool(buf, &value);
55         if (ret)
56                 return ret;
57         ret = fb->simple_enable_set(fb, value);
58         if (ret < 0)
59                 return ret;
60         return n;
61 }
62 static DEVICE_ATTR_RW(enabled);
63
64 static ssize_t card_name_show(struct device *dev, struct device_attribute *attr,
65                               char *buf)
66 {
67         struct fieldbus_dev *fb = dev_get_drvdata(dev);
68
69         /* card_name was provided by child driver. */
70         return sysfs_emit(buf, "%s\n", fb->card_name);
71 }
72 static DEVICE_ATTR_RO(card_name);
73
74 static ssize_t read_area_size_show(struct device *dev,
75                                    struct device_attribute *attr, char *buf)
76 {
77         struct fieldbus_dev *fb = dev_get_drvdata(dev);
78
79         return sysfs_emit(buf, "%zu\n", fb->read_area_sz);
80 }
81 static DEVICE_ATTR_RO(read_area_size);
82
83 static ssize_t write_area_size_show(struct device *dev,
84                                     struct device_attribute *attr, char *buf)
85 {
86         struct fieldbus_dev *fb = dev_get_drvdata(dev);
87
88         return sysfs_emit(buf, "%zu\n", fb->write_area_sz);
89 }
90 static DEVICE_ATTR_RO(write_area_size);
91
92 static ssize_t fieldbus_id_show(struct device *dev,
93                                 struct device_attribute *attr, char *buf)
94 {
95         struct fieldbus_dev *fb = dev_get_drvdata(dev);
96
97         return fb->fieldbus_id_get(fb, buf, PAGE_SIZE);
98 }
99 static DEVICE_ATTR_RO(fieldbus_id);
100
101 static ssize_t fieldbus_type_show(struct device *dev,
102                                   struct device_attribute *attr, char *buf)
103 {
104         struct fieldbus_dev *fb = dev_get_drvdata(dev);
105         const char *t;
106
107         switch (fb->fieldbus_type) {
108         case FIELDBUS_DEV_TYPE_PROFINET:
109                 t = "profinet";
110                 break;
111         default:
112                 t = "unknown";
113                 break;
114         }
115
116         return sysfs_emit(buf, "%s\n", t);
117 }
118 static DEVICE_ATTR_RO(fieldbus_type);
119
120 static struct attribute *fieldbus_attrs[] = {
121         &dev_attr_enabled.attr,
122         &dev_attr_card_name.attr,
123         &dev_attr_fieldbus_id.attr,
124         &dev_attr_read_area_size.attr,
125         &dev_attr_write_area_size.attr,
126         &dev_attr_online.attr,
127         &dev_attr_fieldbus_type.attr,
128         NULL,
129 };
130
131 static umode_t fieldbus_is_visible(struct kobject *kobj, struct attribute *attr,
132                                    int n)
133 {
134         struct device *dev = kobj_to_dev(kobj);
135         struct fieldbus_dev *fb = dev_get_drvdata(dev);
136         umode_t mode = attr->mode;
137
138         if (attr == &dev_attr_enabled.attr) {
139                 mode = 0;
140                 if (fb->enable_get)
141                         mode |= 0444;
142                 if (fb->simple_enable_set)
143                         mode |= 0200;
144         }
145
146         return mode;
147 }
148
149 static const struct attribute_group fieldbus_group = {
150         .attrs = fieldbus_attrs,
151         .is_visible = fieldbus_is_visible,
152 };
153 __ATTRIBUTE_GROUPS(fieldbus);
154
155 static struct class fieldbus_class = {
156         .name =         "fieldbus_dev",
157         .dev_groups =   fieldbus_groups,
158 };
159
160 struct fb_open_file {
161         struct fieldbus_dev *fbdev;
162         int dc_event;
163 };
164
165 static int fieldbus_open(struct inode *inode, struct file *filp)
166 {
167         struct fb_open_file *of;
168         struct fieldbus_dev *fbdev = container_of(inode->i_cdev,
169                                                 struct fieldbus_dev,
170                                                 cdev);
171
172         of = kzalloc(sizeof(*of), GFP_KERNEL);
173         if (!of)
174                 return -ENOMEM;
175         of->fbdev = fbdev;
176         filp->private_data = of;
177         return 0;
178 }
179
180 static int fieldbus_release(struct inode *node, struct file *filp)
181 {
182         struct fb_open_file *of = filp->private_data;
183
184         kfree(of);
185         return 0;
186 }
187
188 static ssize_t fieldbus_read(struct file *filp, char __user *buf, size_t size,
189                              loff_t *offset)
190 {
191         struct fb_open_file *of = filp->private_data;
192         struct fieldbus_dev *fbdev = of->fbdev;
193
194         of->dc_event = fbdev->dc_event;
195         return fbdev->read_area(fbdev, buf, size, offset);
196 }
197
198 static ssize_t fieldbus_write(struct file *filp, const char __user *buf,
199                               size_t size, loff_t *offset)
200 {
201         struct fb_open_file *of = filp->private_data;
202         struct fieldbus_dev *fbdev = of->fbdev;
203
204         return fbdev->write_area(fbdev, buf, size, offset);
205 }
206
207 static __poll_t fieldbus_poll(struct file *filp, poll_table *wait)
208 {
209         struct fb_open_file *of = filp->private_data;
210         struct fieldbus_dev *fbdev = of->fbdev;
211         __poll_t mask = EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM;
212
213         poll_wait(filp, &fbdev->dc_wq, wait);
214         /* data changed ? */
215         if (fbdev->dc_event != of->dc_event)
216                 mask |= EPOLLPRI | EPOLLERR;
217         return mask;
218 }
219
220 static const struct file_operations fieldbus_fops = {
221         .open           = fieldbus_open,
222         .release        = fieldbus_release,
223         .read           = fieldbus_read,
224         .write          = fieldbus_write,
225         .poll           = fieldbus_poll,
226         .llseek         = generic_file_llseek,
227         .owner          = THIS_MODULE,
228 };
229
230 void fieldbus_dev_area_updated(struct fieldbus_dev *fb)
231 {
232         fb->dc_event++;
233         wake_up_all(&fb->dc_wq);
234 }
235 EXPORT_SYMBOL_GPL(fieldbus_dev_area_updated);
236
237 void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online)
238 {
239         fb->online = online;
240         kobject_uevent(&fb->dev->kobj, KOBJ_CHANGE);
241 }
242 EXPORT_SYMBOL_GPL(fieldbus_dev_online_changed);
243
244 static void __fieldbus_dev_unregister(struct fieldbus_dev *fb)
245 {
246         if (!fb)
247                 return;
248         device_destroy(&fieldbus_class, fb->cdev.dev);
249         cdev_del(&fb->cdev);
250         ida_simple_remove(&fieldbus_ida, fb->id);
251 }
252
253 void fieldbus_dev_unregister(struct fieldbus_dev *fb)
254 {
255         mutex_lock(&fieldbus_mtx);
256         __fieldbus_dev_unregister(fb);
257         mutex_unlock(&fieldbus_mtx);
258 }
259 EXPORT_SYMBOL_GPL(fieldbus_dev_unregister);
260
261 static int __fieldbus_dev_register(struct fieldbus_dev *fb)
262 {
263         dev_t devno;
264         int err;
265
266         if (!fb)
267                 return -EINVAL;
268         if (!fb->read_area || !fb->write_area || !fb->fieldbus_id_get)
269                 return -EINVAL;
270         fb->id = ida_simple_get(&fieldbus_ida, 0, MAX_FIELDBUSES, GFP_KERNEL);
271         if (fb->id < 0)
272                 return fb->id;
273         devno = MKDEV(MAJOR(fieldbus_devt), fb->id);
274         init_waitqueue_head(&fb->dc_wq);
275         cdev_init(&fb->cdev, &fieldbus_fops);
276         err = cdev_add(&fb->cdev, devno, 1);
277         if (err) {
278                 pr_err("fieldbus_dev%d unable to add device %d:%d\n",
279                        fb->id, MAJOR(fieldbus_devt), fb->id);
280                 goto err_cdev;
281         }
282         fb->dev = device_create(&fieldbus_class, fb->parent, devno, fb,
283                                 "fieldbus_dev%d", fb->id);
284         if (IS_ERR(fb->dev)) {
285                 err = PTR_ERR(fb->dev);
286                 goto err_dev_create;
287         }
288         return 0;
289
290 err_dev_create:
291         cdev_del(&fb->cdev);
292 err_cdev:
293         ida_simple_remove(&fieldbus_ida, fb->id);
294         return err;
295 }
296
297 int fieldbus_dev_register(struct fieldbus_dev *fb)
298 {
299         int err;
300
301         mutex_lock(&fieldbus_mtx);
302         err = __fieldbus_dev_register(fb);
303         mutex_unlock(&fieldbus_mtx);
304
305         return err;
306 }
307 EXPORT_SYMBOL_GPL(fieldbus_dev_register);
308
309 static int __init fieldbus_init(void)
310 {
311         int err;
312
313         err = class_register(&fieldbus_class);
314         if (err < 0) {
315                 pr_err("fieldbus_dev: could not register class\n");
316                 return err;
317         }
318         err = alloc_chrdev_region(&fieldbus_devt, 0,
319                                   MAX_FIELDBUSES, "fieldbus_dev");
320         if (err < 0) {
321                 pr_err("fieldbus_dev: unable to allocate char dev region\n");
322                 goto err_alloc;
323         }
324         return 0;
325
326 err_alloc:
327         class_unregister(&fieldbus_class);
328         return err;
329 }
330
331 static void __exit fieldbus_exit(void)
332 {
333         unregister_chrdev_region(fieldbus_devt, MAX_FIELDBUSES);
334         class_unregister(&fieldbus_class);
335         ida_destroy(&fieldbus_ida);
336 }
337
338 subsys_initcall(fieldbus_init);
339 module_exit(fieldbus_exit);
340
341 MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
342 MODULE_AUTHOR("Jonathan Stiles <jonathans@arcx.com>");
343 MODULE_DESCRIPTION("Fieldbus Device Driver Core");
344 MODULE_LICENSE("GPL v2");